home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / c / mv.c < prev    next >
Text File  |  1986-09-08  |  72KB  |  2,681 lines

  1.  8-Sep-86 15:43:34-PDT,685;000000000000
  2. Return-Path: <pwu@unix.macc.wisc.edu>
  3. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 15:42:14 PDT
  4. Received: by unix.macc.wisc.edu;
  5.           id AA04894; 4.12/5; Mon, 8 Sep 86 17:29:02 cdt
  6. Date: Mon, 8 Sep 86 17:29:02 cdt
  7. From: Peter Wu <pwu@unix.macc.wisc.edu>
  8. Message-Id: <8609082229.AA04894@unix.macc.wisc.edu>
  9. To: info-ibmpc-request@mosis
  10. Subject: mv source
  11.  
  12. Here comes the mv source. You should receive the following 17 files:
  13.  
  14. absdr.asm
  15. break.c
  16. date.h
  17. dta.h
  18. error.c
  19. front.c
  20. fstat.c
  21. func32h.asm
  22. mv
  23. mv.c
  24. normal.c
  25. peek.h
  26. putn.asm
  27. readme.txt
  28. rm.c
  29. sector.c
  30. testmv.bat
  31.  
  32. I believe you have mv.doc already. If not, let me know.
  33.  
  34. peter
  35.  8-Sep-86 16:33:40-PDT,1479;000000000000
  36. Return-Path: <pwu@unix.macc.wisc.edu>
  37. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:33:28 PDT
  38. Received: by unix.macc.wisc.edu;
  39.           id AA05000; 4.12/5; Mon, 8 Sep 86 17:32:30 cdt
  40. Date: Mon, 8 Sep 86 17:32:30 cdt
  41. From: Peter Wu <pwu@unix.macc.wisc.edu>
  42. Message-Id: <8609082232.AA05000@unix.macc.wisc.edu>
  43. To: info-ibmpc-request@mosis
  44. Subject: readme.txt
  45.  
  46. To make mv from source, you must have ibm C compiler version 1.0 or
  47. Microsoft C version 3.0 plus the macro assembler. Just type
  48.  
  49.   make mv
  50.  
  51. and it will create mv.exe for you. If you use Microsoft C version
  52. 4.0, edit front.c to reverse the order of the parameters in the
  53. "rename" function.
  54.  
  55. Files description
  56.  
  57.   mv        makefile for mv
  58.   absdr.asm    absolute disk read/write BIOS call
  59.   func32h.asm    routine to call an undocumented DOS function
  60.   putn.asm    routine to print strings
  61.   testmv.bat    batch file I used to test mv
  62.   break.c    routines to check and set break status
  63.   error.c    routines to handle fatal errors
  64.   front.c    front end of the mv program
  65.   fstat.c    routines to find matching files and their attributes
  66.   mv.c        routines for moving sub-directories
  67.   normal.c    routines to normalize a path specification
  68.   sector.c    routines to do buffered disk read/write
  69.   mv.exe    executable version of mv
  70.   peek.h    macros for peeking and poking memory
  71.   dta.h     data structure at disk transfer address; used by fstat.c
  72.   date.h    defines the date when the last version was made
  73.  8-Sep-86 15:43:33-PDT,415;000000000000
  74. Return-Path: <pwu@unix.macc.wisc.edu>
  75. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 15:40:13 PDT
  76. Received: by unix.macc.wisc.edu;
  77.           id AA04928; 4.12/5; Mon, 8 Sep 86 17:30:43 cdt
  78. Date: Mon, 8 Sep 86 17:30:43 cdt
  79. From: Peter Wu <pwu@unix.macc.wisc.edu>
  80. Message-Id: <8609082230.AA04928@unix.macc.wisc.edu>
  81. To: info-ibmpc-request@mosis
  82. Subject: date.h
  83.  
  84. #define date "8-20-1986"
  85.  8-Sep-86 15:59:17-PDT,818;000000000000
  86. Return-Path: <pwu@unix.macc.wisc.edu>
  87. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 15:45:12 PDT
  88. Received: by unix.macc.wisc.edu;
  89.           id AA04988; 4.12/5; Mon, 8 Sep 86 17:32:13 cdt
  90. Date: Mon, 8 Sep 86 17:32:13 cdt
  91. From: Peter Wu <pwu@unix.macc.wisc.edu>
  92. Message-Id: <8609082232.AA04988@unix.macc.wisc.edu>
  93. To: info-ibmpc-request@mosis
  94. Subject: peek.h
  95.  
  96. /* pseudo functions to peek/poke byte or word. Written by Peter Wu. 6/5/86.
  97. ** Use /ze option when compiling.
  98. */
  99.  
  100. #define acc(seg,off) ((long) (seg) << 16 | (unsigned short) (off))
  101. #define peekb(seg,off) (*(unsigned char far *)acc(seg,off))
  102. #define pokeb(seg,off,val) (*(char far *)acc(seg,off) = (val))
  103. #define peekw(seg,off) (*(short far *)acc(seg,off))
  104. #define pokew(seg,off,val) (*(short far *)acc(seg,off) = (val))
  105.  8-Sep-86 16:13:30-PDT,2449;000000000000
  106. Return-Path: <pwu@unix.macc.wisc.edu>
  107. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:09:00 PDT
  108. Received: by unix.macc.wisc.edu;
  109.           id AA04966; 4.12/5; Mon, 8 Sep 86 17:31:32 cdt
  110. Date: Mon, 8 Sep 86 17:31:32 cdt
  111. From: Peter Wu <pwu@unix.macc.wisc.edu>
  112. Message-Id: <8609082231.AA04966@unix.macc.wisc.edu>
  113. To: info-ibmpc-request@mosis
  114. Subject: func32h.asm
  115.  
  116. ; Call DOS 32H from C. This is an undocumented function call documented in
  117. ; PC Tech. Journal May 1986. This function returns a pointer to a disk
  118. ; description table that contains the following:
  119. ;
  120. ;   offset  length  what
  121. ;   ------  ------  ----
  122. ;      0     byte   assigned physical disk (A=0, B=1, ...)
  123. ;      1     byte   same as above but 0 for RAM disk
  124. ;      2     word   bytes per sector
  125. ;      4     byte   sectors per cluster minus 1
  126. ;      5     byte   #heads minus 1
  127. ;      6     word   reserved sectors
  128. ;      8     byte   #copies of FAT (normally 2 for real disks, 1 for RAM disks)
  129. ;      9     word   max directory entries
  130. ;     11     word   first usable sector (i.e. data area)
  131. ;     13     word   total cluster count plus 1
  132. ;     15     byte   #sectors occupied by each FAT
  133. ;     16     word   first sector of the root's directory
  134. ;     18    dword   device driver address
  135. ;     22     word   media descriptor
  136. ;     24    dword   chain to next disk table
  137. ;     28     word   cluster of current working directory
  138. ;
  139. ; in C, use
  140. ;   unsigned char drv,    /* drive number: 1=A, 2=B, ...; 0=current drive */
  141. ;          status;  /* if 0xFF means invalid drive */
  142. ;   unsigned short tabseg,  /* disk description table segment */
  143. ;           taboff;  /* disk description table offset */
  144. ;
  145. ;   status = func32h(drv, &tabseg, &taboff);
  146. ;
  147. ; The reason for writing this procedure in assembly is because the
  148. ; function returns the description table segment address in DS which
  149. ; cannot be accessed by using intdosx() function in C.
  150. ;
  151. ; Written by Peter Wu 6/27/86
  152. ;
  153. _text    segment public byte 'code'
  154.     assume cs:_text
  155.  
  156.     public    _func32h
  157. _func32h proc     near
  158.     push    bp
  159.     mov    bp,sp
  160.     push    ds        ; save DS
  161.  
  162.     mov    dl,[bp+4]    ; drive number
  163.     mov    ah,32h
  164.     int    21h
  165.  
  166.     mov    cx,bx        ; stupid move, but I need bx
  167.     mov    si,ds
  168.     pop    ds
  169.  
  170.     mov    bx,[bp+6]    ; &tabseg
  171.     mov    [bx],si     ; store tabseg
  172.     mov    bx,[bp+8]    ; &taboff
  173.     mov    [bx],cx     ; store taboff
  174.     mov    ah,0        ; al contains ffh if error
  175.  
  176.     pop    bp
  177.     ret
  178. _func32h endp
  179.  
  180. _text    ends
  181.     end
  182.  8-Sep-86 16:13:31-PDT,2971;000000000000
  183. Return-Path: <pwu@unix.macc.wisc.edu>
  184. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:09:21 PDT
  185. Received: by unix.macc.wisc.edu;
  186.           id AA05017; 4.12/5; Mon, 8 Sep 86 17:32:54 cdt
  187. Date: Mon, 8 Sep 86 17:32:54 cdt
  188. From: Peter Wu <pwu@unix.macc.wisc.edu>
  189. Message-Id: <8609082232.AA05017@unix.macc.wisc.edu>
  190. To: info-ibmpc-request@mosis
  191. Subject: sector.c
  192.  
  193. /* Routines to read and write sectors
  194. ** Only one copy of a particular sector will be stored, to prevent
  195. ** inconsistency.
  196. ** Written by Peter Wu 7/11/86 @ Faculty Support Center @ Univ. of Wisconsin
  197. ** This is used by the mv (move files and directory) program.
  198. ** Link with absdr (source absdr.asm)
  199. */
  200. #define LINT_ARGS
  201.  
  202. #define  MAX_SECTOR_SIZE  4200    /* is this safe enough? */
  203. #define  MAX_COPIES  5        /* max # of sectors buffered */
  204.  
  205. #include <conio.h>
  206.  
  207. /* global stuff */
  208.  
  209. char secbufx[MAX_SECTOR_SIZE * MAX_COPIES], dirty[MAX_COPIES];
  210. int secind[MAX_COPIES], drvind[MAX_COPIES];
  211. int num_sec;  /* number of sectors currently buffered */
  212.  
  213. char *readsec(drv, sector)  /* read a sector; return pointer to buffer */
  214. int drv, sector;
  215. {
  216.   int i, status;
  217.   char *where;
  218.  
  219. #ifdef debug
  220.   printf("reading sector# %d\n",sector);
  221. #endif
  222.   /* first see if the sector requested is already in buffer */
  223.   for (i=0; i < num_sec; i++) {
  224.     if ((secind[i] == sector) &&
  225.     (drvind[i] == drv)    ) {  /* aha - sector already read */
  226.       return secbufx + i * MAX_SECTOR_SIZE;  /* return pointer to buffer */
  227.     }
  228.   }
  229.  
  230.   /* sector is not in buffer, so read it, but first allocate a buffer */
  231.   secind[num_sec] = sector;  /* store sector number */
  232.   drvind[num_sec] = drv;
  233.   where = secbufx + num_sec * MAX_SECTOR_SIZE;
  234.   status = absdr(drv, 1, sector, where);
  235.   if (status) {
  236.     cputs("Error in readsec calling absdr\n\015");
  237.     return (char *) 0;
  238.   }
  239.   dirty[num_sec] = 0;  /* not dirty */
  240.   num_sec++;
  241.  
  242.   /* successfully read a sector into buffer */
  243.   return where;
  244. }
  245.  
  246. writesec(drv,sector)
  247. int drv, sector;
  248. {
  249.   int i, status;
  250.  
  251. #ifdef debug
  252.   printf("writing sector# %d\n", sector);
  253. #endif
  254.   for (i=0; i < num_sec; i++) {
  255.     if ((secind[i] == sector) && (drvind[i] == drv)) {
  256.       if (dirty[i]) {  /* write the sector only if it's dirty */
  257.     status = absdw(drvind[i], 1, secind[i], secbufx + i * MAX_SECTOR_SIZE);
  258.     if (status) {
  259.       cputs("Error in flushsec calling absdw\n\015");
  260.       return -1;
  261.     } else {
  262.       dirty[i] = 0;
  263.     }
  264.       }
  265.       return 0;
  266.     }
  267.   }  /* for */
  268.   cputs("writesec: sector is not in buffer\n\015");
  269.   return -2;
  270. }
  271.  
  272. flirt(drv,sector)  /* turn on dirty flag */
  273. int drv,sector;
  274. {
  275.   int i;
  276.  
  277. #ifdef debug
  278.   printf("flirting sector# %d; num_sec = %d\n",sector,num_sec);
  279. #endif
  280.   for (i=0; i < num_sec; i++) {
  281.     if ((secind[i] == sector) && (drvind[i] == drv)) {
  282.       dirty[i] = 1;
  283.       return 0;
  284.     }
  285.   }
  286.   cputs("flirt: can't find sector to flirt with\n\015");
  287.   return -1;
  288. }
  289.  8-Sep-86 16:13:34-PDT,3102;000000000000
  290. Return-Path: <pwu@unix.macc.wisc.edu>
  291. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:09:52 PDT
  292. Received: by unix.macc.wisc.edu;
  293.           id AA04934; 4.12/5; Mon, 8 Sep 86 17:30:54 cdt
  294. Date: Mon, 8 Sep 86 17:30:54 cdt
  295. From: Peter Wu <pwu@unix.macc.wisc.edu>
  296. Message-Id: <8609082230.AA04934@unix.macc.wisc.edu>
  297. To: info-ibmpc-request@mosis
  298. Subject: dta.h
  299.  
  300. #define A_FIL    0x40  /* Peter's addition: this mask searches for file */
  301. #define A_ARC    0x20  /* attributes bits of attribute byte in dta */
  302. #define A_DIR    0x10
  303. #define A_SYS    0x4
  304. #define A_HID    0x2
  305. #define A_MASK    (A_FIL | A_DIR | A_SYS | A_HID)  /* mask to find all files */
  306.  
  307. struct dtbuf3 {  /* structure returned by dos function 0x4e and 0x4f */
  308.   /* The first 21 bytes are undocumented; use them at your own risk.
  309.   ** Their use is guessed by Peter Wu. This applies only to DOS 3.xx
  310.   */
  311.  
  312.   unsigned char drv_no;      /* drive number; 1=A  2=B    3=C ... */
  313.   char template[11];         /* file template; no period */
  314.   unsigned char match_attr;  /* the search attribute */
  315.   unsigned char slotl;         /* directory slot number of the matching file */
  316.   unsigned char sloth;
  317.   unsigned char clusl, clush; /* cluster number of the directory being
  318.                  searched */
  319.   unsigned char unknown[4];   /* haven't figured out what these are */
  320.   /* end of first 21 bytes */
  321.  
  322.   unsigned char attr;
  323.   unsigned short time;
  324.   unsigned short data;
  325.   unsigned long size;  /* 4 bytes */
  326.   char fn[13];    /* this is an asciiz */
  327.   unsigned char e_attr;  /* extended (by peter) search attribute */
  328. };
  329.  
  330. struct dtbuf2 {  /* structure returned by dos function 0x4e and 0x4f */
  331.   /* The first 21 bytes are undocumented; use them at your own risk.
  332.   ** Their use is guessed by Peter Wu. This applies only to DOS 2.xx
  333.   */
  334.  
  335.   unsigned char match_attr;  /* the search attribute */
  336.   unsigned char drv_no;      /* drive number; 1=A  2=B    3=C ... */
  337.   char template[11];         /* file template; no period */
  338.   unsigned char slotl;         /* directory slot number of the matching file */
  339.   unsigned char sloth;
  340.   unsigned char unknown[4];   /* haven't figured out what these are */
  341.   unsigned char clusl, clush; /* cluster number of the directory being
  342.                  searched */
  343.   /* end of first 21 bytes */
  344.  
  345.   unsigned char attr;
  346.   unsigned short time;
  347.   unsigned short data;
  348.   unsigned long size;  /* 4 bytes */
  349.   char fn[13];    /* this is an asciiz */
  350.   unsigned char e_attr;  /* extended (by peter) search attribute */
  351. };
  352.  
  353. struct dtbufx {  /* generic version (works for any DOS version */
  354.   unsigned char unknown[21];
  355.   unsigned char attr;
  356.   unsigned short time;
  357.   unsigned short data;
  358.   unsigned long size;  /* 4 bytes */
  359.   char fn[13];    /* this is an asciiz */
  360.   unsigned char e_attr;  /* extended (by peter) search attribute */
  361.  
  362.   /* the following info has to be copied from dtbuf2 or dtbuf3 by
  363.   ** the ffmf and fnmf functions
  364.   */
  365.   unsigned char drv_no;
  366.   unsigned char slotl, sloth, clusl, clush;
  367. };
  368.  
  369. union dtbuf {
  370.   struct dtbuf3 dos3;
  371.   struct dtbuf2 dos2;
  372.   struct dtbufx dos;
  373. };
  374.  8-Sep-86 16:13:36-PDT,4915;000000000000
  375. Return-Path: <pwu@unix.macc.wisc.edu>
  376. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:10:13 PDT
  377. Received: by unix.macc.wisc.edu;
  378.           id AA04962; 4.12/5; Mon, 8 Sep 86 17:31:25 cdt
  379. Date: Mon, 8 Sep 86 17:31:25 cdt
  380. From: Peter Wu <pwu@unix.macc.wisc.edu>
  381. Message-Id: <8609082231.AA04962@unix.macc.wisc.edu>
  382. To: info-ibmpc-request@mosis
  383. Subject: fstat.c
  384.  
  385. /* find first matching file and find next matching file
  386. ** written by Peter Wu @ Faculty Support Center @ Univ. of Wisconsin
  387. ** July 1986
  388. **
  389. ** These functions have an extended capability to search for
  390. ** directories only and ffmf does not return "." and "..", also
  391. ** ffmf will attempt to find root directory and return at least drive number
  392. */
  393. #define LINT_ARGS
  394.  
  395. #include "dta.h"
  396. #include <dos.h>
  397. #include <stdlib.h>
  398.  
  399. char lastc(char *);
  400.  
  401. ffmf(fn,attr,dta)  /* extended version of ffmf1; fn must be normalized */
  402. char *fn;
  403. short attr;
  404. union dtbuf *dta;
  405. {
  406.   int status, len;
  407.   char tmpfn[200];
  408.   union dtbuf tmpdta;
  409.  
  410.   if (lastc(fn) != '\\') {  /* if not looking for root directory */
  411.  
  412.     status = ffmf1(fn,attr,dta);
  413.     if (status) {
  414.       return status;
  415.     }
  416.  
  417.     /* get rid of "." and ".." */
  418.     while (dta->dos.fn[0] == '.') {
  419.       status = fnmf1(dta);
  420.       if (status) {
  421.     return status;
  422.       }
  423.     }
  424.  
  425.     /* now check to see if we should return plain files or not */
  426.     while ( ((dta->dos.e_attr & A_FIL) == 0) &&
  427.         ((dta->dos.attr & A_DIR) == 0) ) {
  428.       /* user didn't ask for plain files and we found one, so skip it */
  429.       status = fnmf1(dta);
  430.       if (status) {
  431.     return status;
  432.       }
  433.     }
  434.  
  435.     fixdta(dta,dta);
  436.     return 0;
  437.   }
  438.  
  439.   /* fn ends with '\', i.e. looking for root directory. In DOS 3 ffmf1 won't
  440.   ** find root directory, and in DOS 2 ffmf1 found root directory but reports
  441.   ** it to have file status! Neither is right. So I have to fix it here.
  442.   ** The reason for wanting to look for a root directory is to find out
  443.   ** what drive it's on.
  444.   */
  445.  
  446.   if (attr & A_DIR) {
  447.     /* look for '\*.*' */
  448.     strcpy(tmpfn, fn);
  449.     strcat(tmpfn, "*.*");
  450.     status = ffmf1(tmpfn, A_DIR | A_FIL, &tmpdta);
  451.     if (status) {  /* can't even find root this way: root must be empty */
  452.       cputs("empty root directory; nothing to move\n\015");
  453.       exit(1);
  454.     }
  455.  
  456.     /* found root; now fill in dta */
  457.     dta->dos.fn[0] = '\0';
  458.     dta->dos.attr = A_DIR;
  459.     fixdta(&tmpdta,dta);
  460.     return 0;
  461.   } else {
  462.     return 1; /* can't find root because attr is not set to A_DIR */
  463.   }
  464. }
  465.  
  466. /* extended version of fnmf1; because of the way ffmf handle root directory,
  467. ** calling ffmf to find root directory and then call fnmf will cause an
  468. ** "allocation table bad" error
  469. */
  470. fnmf(dta)
  471. union dtbuf *dta;
  472. {
  473.   int status;
  474.  
  475.   status = fnmf1(dta);
  476.   if (status) {
  477.     return status;
  478.   }
  479.  
  480.   while (((dta->dos.e_attr & A_FIL) == 0) && ((dta->dos.attr & A_DIR) == 0)) {       /* user didn't ask for plain file and we found one, so skip it */
  481.     status = fnmf1(dta);
  482.     if (status) {
  483.       return status;
  484.     }
  485.   }
  486.  
  487.   fixdta(dta,dta);
  488.   return 0;  /* no error */
  489. }
  490.  
  491. /* copy dos dependent dta fields to the dos independent dta fields */
  492. fixdta(from, to)
  493. union dtbuf *from, *to;
  494. {
  495.   switch (_osmajor) {
  496.  
  497.     case 3:  /* dos 3.xx */
  498.       to->dos.drv_no = from->dos3.drv_no;
  499.       to->dos.slotl = from->dos3.slotl;
  500.       to->dos.sloth = from->dos3.sloth;
  501.       to->dos.clusl = from->dos3.clusl;
  502.       to->dos.clush = from->dos3.clush;
  503.  
  504.       to->dos3.drv_no = from->dos3.drv_no;  /* for compatiblity sake */
  505.       break;
  506.  
  507.     case 2:  /* dos 2.xx */
  508.       to->dos.drv_no = from->dos2.drv_no + 1;
  509.       to->dos.slotl = from->dos2.slotl;
  510.       to->dos.sloth = from->dos2.sloth;
  511.       to->dos.clusl = from->dos2.clusl;
  512.       to->dos.clush = from->dos2.clush;
  513.  
  514.       to->dos2.drv_no = from->dos2.drv_no;  /* for compatiblity sake */
  515.       break;
  516.  
  517.     default:
  518.       cputs("unexpected DOS version\n\015");
  519.       error("fixdta", 0);
  520.   }
  521. }
  522.  
  523. ffmf1(fn,attr,dta)  /* don't call this, call ffmf */
  524. char *fn;
  525. short attr;
  526. union dtbuf *dta;
  527. {
  528.   union REGS inregs,outregs;
  529.  
  530.   dta->dos.e_attr = attr;  /* store the extended attribute */
  531.   bdos(0x1a, (int) dta, 0);  /* set dta */
  532.  
  533.   inregs.h.ah = 0x4e;
  534.   inregs.x.dx = (int) fn;
  535.   inregs.x.cx = attr & 0x3f;  /* mask off the A_FIL bit */
  536.   intdos(&inregs, &outregs);  /* now find first entry */
  537.  
  538.   if (outregs.x.cflag) {
  539.     return outregs.x.ax;  /* return error code */
  540.   }
  541.   return 0;  /* no error */
  542. }
  543.  
  544. fnmf1(dta)  /* find next matching file */
  545. union dtbuf *dta;
  546. {
  547.   union REGS inregs,outregs;
  548.  
  549.   bdos(0x1a, (int) dta, 0);  /* set dta */
  550.  
  551.   inregs.h.ah = 0x4f;
  552.   intdos(&inregs, &outregs);  /* now find next entry */
  553.  
  554.   if (outregs.x.cflag) {
  555.     return (outregs.x.ax);
  556.   } else {
  557.     return (0);
  558.   };
  559. }
  560.  8-Sep-86 16:13:37-PDT,7276;000000000000
  561. Return-Path: <pwu@unix.macc.wisc.edu>
  562. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:10:31 PDT
  563. Received: by unix.macc.wisc.edu;
  564.           id AA05007; 4.12/5; Mon, 8 Sep 86 17:32:42 cdt
  565. Date: Mon, 8 Sep 86 17:32:42 cdt
  566. From: Peter Wu <pwu@unix.macc.wisc.edu>
  567. Message-Id: <8609082232.AA05007@unix.macc.wisc.edu>
  568. To: info-ibmpc-request@mosis
  569. Subject: rm.c
  570.  
  571. /* rm - removes everything you got
  572. ** Written by Peter Wu @ Faculty Support Center, MACC
  573. ** University of Wisconsin - Madison. August 1986.
  574. */
  575. #include "dta.h"
  576. #include <conio.h>
  577. #include <ctype.h>
  578. #include "date.h"
  579.  
  580. char getkey();
  581.  
  582. main(argc, argv)
  583. int argc;
  584. char *argv[];
  585. {
  586.   int brgc, optc, i, status, mask;
  587.   char *brgv[40], opt[26], *t, path[200], c;
  588.  
  589.   optc = 0;
  590.   brgc = 0;  /* my argc */
  591.  
  592.   for (i=0; i < 26; i++) {  /* clear options flags */
  593.     opt[i] = 0;
  594.   };
  595.  
  596.   for (i=1; i < argc; i++) {  /* separate options and arguments */
  597.     if ((*argv[i] == '/') || (*argv[i] == '-')) {
  598.       t = argv[i] + 1;
  599.       while ((c = *t++) != '\0') {
  600.     if (isalpha(c)) {
  601.       opt[tolower(c) - 'a'] = 1;
  602.     };
  603.       };
  604.     } else {
  605.       brgv[brgc++] = argv[i];
  606.     };
  607.   };
  608.  
  609.   if (brgc == 0) {
  610.     putn("Usage: RM <filespec>\15\n",
  611.      "<filespec> can have wild cards and attributes /f /d /h; E.g:\15\n",
  612.      "  *.* or *.*/f  selects all visible file\15\n",
  613.      "  *.*/h or *.*/hf  selects all visible and invisible files\15\n",
  614.      "  *.*/d  selects all sub-directories\15\n\n",
  615.      "For each file/sub-directory found, you will be prompted for\15\n",
  616.      "one of the following actions:\15\n",
  617.      "   y - yes, delete this file\15\n",
  618.      "   Y - Yes, delete this sub-directory\15\n",
  619.      "   n - no, leave this file/sub-directory alone\15\n",
  620.      "   l - list all the rest of the files/sub-directories\15\n",
  621.      "   Z - delete ALL the rest of the files/sub-directories\15\n",
  622.      "   e - enter this sub-directory\15\n",
  623.      "   x - exit current sub-directory\15\n",
  624.      "   q - quit now\15\n\n",
  625.      "Version 2.00 made ", date, ".\15\n",
  626.      "Please report problems to Peter Wu.\15\n",
  627.      0);
  628.     exit(0);
  629.   }
  630.  
  631.   /* process parameters */
  632.   for (i=0; i < brgc; i++) {
  633.     strcpy(path, brgv[i]);
  634.     mask = extype(path);
  635.     mask |= normal(path);
  636.     if ((mask & (A_FIL | A_DIR)) == 0) {
  637.       mask |= A_FIL;  /* default is remove files only */
  638.     }
  639.     rm(1, 1, mask, path);
  640.   }
  641. }
  642.  
  643. rm(confirm, echo, mask, path)  /* use normalized path only */
  644. int confirm, mask;
  645. char *path;
  646. {
  647.   char headp[200], fullp[200], c;
  648.   int status;
  649.   union dtbuf mydta;
  650.  
  651.   strcpy(headp,path);
  652.   chopath(headp);  /* cut off the tail (might be wild-cards) */
  653.  
  654.   status = ffmf(path, mask, &mydta);
  655.   if (status) {  /* not found; probably empty directory */
  656.     return 1;  /* let the caller print out the error message */
  657.   }
  658.  
  659.   do {    /* for all matching files */
  660.  
  661.     strcpy(fullp, headp);
  662.     catpath(fullp, mydta.dos.fn);  /* now fullp has the expanded name */
  663.  
  664.     do {
  665.  
  666.       if (confirm | echo) {  /* echo files to be deleted */
  667.     cputs(fullp);
  668.     if (mydta.dos.attr & A_DIR) {
  669.       cputs(" <DIR>");
  670.     }
  671.     if (mydta.dos.attr & (A_HID | A_SYS)) {
  672.       cputs(" (hidden)");
  673.     }
  674.       }
  675.  
  676.       if (confirm) {  /* ask for confirmation */
  677.  
  678.     status = 0;
  679.     if (mydta.dos.attr & A_DIR) {  /* sub-dir */
  680.       cputs(" (Y/n/e/x/l/Z/q; ? for help): ");
  681.       c = getkey("YynlxqeZ?");
  682.     } else {  /* file */
  683.       cputs(" (y/n/x/l/Z/q; ? for help): ");
  684.       c = getkey("YynlxqZx?");
  685.     }
  686.  
  687.     switch (c) {
  688.  
  689.       case '?':
  690.         putn("\15\n",
  691.          "Y - Yes, delete this sub-directory and its content\15\n",
  692.          "y - yes, delete this file\15\n",
  693.          "n - no, skip this file/sub-directory\15\n",
  694.          "e - enter this sub-directory\15\n",
  695.          "x - exit current sub-directory\15\n",
  696.          "l - list all the rest of the files/sub-directories\15\n",
  697.          "Z - delete ALL the rest without further prompts\15\n",
  698.          "q - quit to DOS now\15\n",
  699.          0);
  700.          cputs("\n");
  701.         break;
  702.  
  703.       case 'Z':
  704.         putn("\15\n",
  705.            "Really delete ALL the remaning files/sub-directories (Y/N)? ",
  706.            0);
  707.         c = getkey("YNn");
  708.         if (c == 'Y') {
  709.           confirm = 0;  /* no more confirmation request */
  710.           status = 1;  /* exit loop */
  711.         }
  712.         cputs("\15\n");
  713.         break;
  714.  
  715.       case 'q':
  716.         cputs("\15\nquiting to DOS\15\n");
  717.         exit(0);
  718.  
  719.       case 'x':
  720.         cputs("\15\n");
  721.         return 2;  /* exit current directory */
  722.  
  723.       case 'y':
  724.         if (mydta.dos.attr & A_DIR) { /* can't delete sub-dir with 'y' */
  725.           cputs(" use Y to delete sub-directory\15\n");
  726.         } else {  /* delete this file */
  727.           putch(' ');  /* move the cursor */
  728.           status = 1;  /* delete */
  729.         }
  730.         break;
  731.  
  732.       case 'Y':
  733.         if (mydta.dos.attr & A_DIR) {
  734.           putch(' ');  /* move the cursor */
  735.           status = 1;  /* delete */
  736.         } else {  /* can't delete file with 'Y' */
  737.           cputs(" use y to delete file\15\n");
  738.         }
  739.         break;
  740.  
  741.       case 'n':
  742.         status = 2;
  743.         break;
  744.  
  745.       case 'l':
  746.         cputs("\15\n");
  747.         ls(mydta);
  748.         break;
  749.  
  750.       case 'e':
  751.         cputs(" entering sub-directory\15\n\n");
  752.         catpath(fullp,"*.*");
  753.         if (rm(1,1,A_MASK,fullp) == 1) {  /* empty */
  754.           cputs("This sub-directory is empty. ");
  755.         }
  756.         chopath(fullp);
  757.         cputs("Returning to previous directory.\15\n\n");
  758.         break;
  759.  
  760.       default:  /* what did I miss? */
  761.         cputs("invalid key\15\n");
  762.     }
  763.  
  764.       } else {    /* confirm == 0 */
  765.     status = 1;
  766.       }
  767.  
  768.     } while (!status);
  769.  
  770.     /* status == 1 delete; status == 2 don't delete */
  771.  
  772.     if (status == 1) {    /* delete */
  773.       if (mydta.dos.attr & A_DIR) {  /* delete sub-directory */
  774.     /* check if fullp is parent of current path */
  775.  
  776.     /* call rm recursively to remove stuff */
  777.     catpath(fullp,"*.*");
  778.     rm(0,0,A_MASK,fullp);  /* no confirmation for this */
  779.     chopath(fullp);  /* cut off the "*.*" */
  780.     status = rmdir(fullp);
  781.     if (echo || confirm) {
  782.       if (status) {
  783.         cputs(" can't delete\15\n");
  784.       } else {
  785.         cputs(" Deleted\15\n");
  786.       }
  787.     }
  788.  
  789.       } else {    /* delete files */
  790.  
  791.     status = unlink(fullp);
  792.     if (echo || confirm) {
  793.       if (status) {
  794.         cputs(" can't delete\15\n");
  795.       } else {
  796.         cputs(" deleted\15\n");
  797.       }
  798.     }
  799.       }
  800.  
  801.     } else {
  802.       cputs(" not deleted\15\n");
  803.     }
  804.  
  805.     status = fnmf(&mydta);
  806.   } while (status == 0);
  807.  
  808.   return 0;
  809. }
  810.  
  811. ls(cdta)  /* list the rest of the files */
  812. union dtbuf cdta;
  813. {
  814.   int i, status, col, slen;  /* column */
  815.  
  816.   col = 0;
  817.   do {    /* list the current one also */
  818.     col++;
  819.     slen = strlen(cdta.dos.fn);
  820.     cputs(cdta.dos.fn);
  821.     if (cdta.dos.attr & A_DIR) {
  822.       putch('\\');
  823.       slen++;
  824.     };
  825.  
  826.     if (col < 5) {
  827.       for (i=slen; i < 16; i++) {
  828.     putch(' ');
  829.       }
  830.     } else {
  831.       cputs("\15\n");
  832.       col = 0;
  833.     }
  834.     status = fnmf(&cdta);
  835.   } while (!status);
  836.  
  837.   if (col) {  /* complete last line */
  838.     cputs("\15\n");
  839.   }
  840. }
  841.  
  842. char getkey(valid)  /* wait for valid key and echo it */
  843. char *valid;
  844. {
  845.   char c;
  846.   int i;
  847.  
  848.   do {
  849.     c = getch();
  850.     i = index(valid,c);
  851.     if (i > -1) {
  852.       putch(c);  /* echo valid key */
  853.       return c;
  854.     } else {  /* beep at invalid key */
  855.       putch(7);
  856.     }
  857.   } while (1);
  858. }
  859.  8-Sep-86 16:13:38-PDT,8378;000000000000
  860. Return-Path: <pwu@unix.macc.wisc.edu>
  861. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:10:58 PDT
  862. Received: by unix.macc.wisc.edu;
  863.           id AA05024; 4.12/5; Mon, 8 Sep 86 17:33:02 cdt
  864. Date: Mon, 8 Sep 86 17:33:02 cdt
  865. From: Peter Wu <pwu@unix.macc.wisc.edu>
  866. Message-Id: <8609082233.AA05024@unix.macc.wisc.edu>
  867. To: info-ibmpc-request@mosis
  868. Subject: testmv.bat
  869.  
  870. echo off
  871. rem *** run this batch file if you suspect mv does not work properly on
  872. rem *** your system
  873. echo This batch file is used to test MV. Run in an empty directory.
  874. echo Make sure path points to MV.
  875. echo 
  876. if %2.==root. goto ok
  877. if %2.==notroot. goto ok
  878. goto noarg
  879. :ok
  880. echo > a
  881. if not exist %1:a goto userr
  882. pause
  883.  
  884. echo The DOS is
  885. ver
  886.  
  887. rem *** create files and directories to play with
  888. echo Creating files and sub-directories to test MV
  889. echo > b
  890. echo > c
  891. echo > e
  892. md f
  893. echo > f\x.x
  894. md g
  895. echo > g\y.y
  896.  
  897. rem *** test #1 - #19 exercise the "front" module
  898.  
  899. echo test#1 (file rename)
  900. rem *** should not cause any error
  901. mv e d > mv.log
  902. if errorlevel 1 goto err
  903. if exist e goto wrong
  904. if not exist d goto wrong
  905.  
  906. echo test#2 (sub-directory rename)
  907. rem *** should not cause any error
  908. mv f e > mv.log
  909. if errorlevel 1 goto err
  910. if exist f\x.x goto wrong
  911. if not exist e\x.x goto wrong
  912.  
  913. echo test#3 (try to rename file to another existing file)
  914. rem *** MV should detect error
  915. mv a b > mv.log
  916. if errorlevel 2 goto err
  917. if not errorlevel 1 goto noerr
  918. if not exist a goto wrong
  919.  
  920. echo test#4 (try to move multiple sources to a file name)
  921. rem *** MV should detect error
  922. mv * x. > mv.log
  923. if errorlevel 2 goto err
  924. if not errorlevel 1 goto noerr
  925. if exist x goto wrong
  926.  
  927. echo test#5 (similar to test#4)
  928. rem *** MV should detect error
  929. mv a b c > mv.log
  930. if errorlevel 2 goto err
  931. if not errorlevel 1 goto noerr
  932. if not exist a goto wrong
  933. if not exist b goto wrong
  934.  
  935. echo test#6 (try to move multiple source to a name)
  936. rem *** MV should detect error
  937. mv * x > mv.log
  938. if errorlevel 2 goto err
  939. if not errorlevel 1 goto noerr
  940. if exist x goto wrong
  941.  
  942. echo test#7 (try to move file into non-existing sub-directory)
  943. rem *** MV should detect error
  944. mv a f\. > mv.log
  945. if errorlevel 2 goto err
  946. if not errorlevel 1 goto noerr
  947. if not exist a goto wrong
  948.  
  949. echo test#8 (destination path not exist)
  950. rem *** MV should detect error
  951. mv a f\a > mv.log
  952. if errorlevel 2 goto err
  953. if not errorlevel 1 goto noerr
  954. if not exist a goto wrong
  955.  
  956. echo test#9 (destination path includes a file)
  957. rem *** MV should detect error
  958. mv a b\c > mv.log
  959. if errorlevel 2 goto err
  960. if not errorlevel 1 goto noerr
  961. if not exist a goto wrong
  962.  
  963. echo test#10 (destination specified as sub-directory but exists as a file)
  964. rem *** MV should detect error
  965. mv a b\. > mv.log
  966. if errorlevel 2 goto err
  967. if not errorlevel 1 goto noerr
  968. if not exist a goto wrong
  969.  
  970. echo test#11 (destination is ambigious)
  971. rem *** MV should detect error
  972. mv a * > mv.log
  973. if errorlevel 2 goto err
  974. if not errorlevel 1 goto noerr
  975. if not exist a goto wrong
  976.  
  977. echo test#12 (similar to test#11)
  978. rem *** MV should detect error
  979. mv a *\. > mv.log
  980. if errorlevel 2 goto err
  981. if not errorlevel 1 goto noerr
  982. if not exist a goto wrong
  983.  
  984. echo test#13 (rename file to itself)
  985. rem *** MV should detect error
  986. mv a . > mv.log
  987. if errorlevel 1 goto err
  988. if not exist a goto wrong
  989.  
  990. echo test#14 (source not exist)
  991. rem *** MV should detect error
  992. mv x y > mv.log
  993. if errorlevel 1 goto err
  994. if exist y goto wrong
  995.  
  996. echo test#15 (move files and sub-directory into sub-directory)
  997. rem *** no error
  998. mv *. e > mv.log
  999. if errorlevel 1 goto err
  1000. if not exist e\a goto wrong
  1001. if not exist e\b goto wrong
  1002. if not exist e\c goto wrong
  1003. if not exist e\d goto wrong
  1004. if not exist e\g\y.y goto wrong
  1005. if exist a goto wrong
  1006. if exist b goto wrong
  1007. if exist c goto wrong
  1008. if exist d goto wrong
  1009. if exist g\y.y goto wrong
  1010.  
  1011. echo test#16 (move sub-directory to current directory)
  1012. rem *** no error
  1013. mv e\g . > mv.log
  1014. if errorlevel 1 goto err
  1015. if not exist g\y.y goto wrong
  1016. if exist e\g\y.y goto wrong
  1017.  
  1018. echo test#17 (move one sub-directory into another)
  1019. rem *** no error
  1020. mv e g > mv.log
  1021. if errorlevel 1 goto err
  1022. if not exist g\e\a goto wrong
  1023. if exist e\a goto wrong
  1024.  
  1025. echo test#18 (move file to current directory)
  1026. rem *** no error
  1027. mv g\e\a .\a > mv.log
  1028. if errorlevel 1 goto err
  1029. if not exist a goto wrong
  1030. if exist g\e\a goto wrong
  1031.  
  1032. echo test#19 (multiple sources with one missing source)
  1033. mv g\e\a g\e\b g\e\* . > mv.log
  1034. if errorlevel 1 goto err
  1035. if not exist b goto wrong
  1036. if not exist c goto wrong
  1037. if not exist d goto wrong
  1038.  
  1039. rem *** the following tests exercise the "mv" module
  1040.  
  1041. echo test#20 (try to move current directory)
  1042. rem *** should cause error
  1043. mv . x > mv.log
  1044. if errorlevel 2 goto err
  1045. rem *** if current directory is root it'll cause errorlevel 1;
  1046. if %2==root if not errorlevel 1 goto noerr
  1047. rem *** otherwise no errorlevel
  1048. if %2==notroot if errorlevel 1 goto err
  1049.  
  1050. echo test#21 (try to move parent into child)
  1051. rem *** should cause error but no errorlevel
  1052. mv g g\e > mv.log
  1053. if errorlevel 1 goto err
  1054. if not exist g\y.y goto wrong
  1055.  
  1056. echo test#22 (similar to test#21)
  1057. mv g g\e\egg > mv.log
  1058. if errorlevel 1 goto err
  1059. if not exist g\y.y goto wrong
  1060.  
  1061. echo test#23 (similar to test#21)
  1062. mv g g > mv.log
  1063. if errorlevel 1 goto err
  1064. if not exist g\y.y goto wrong
  1065.  
  1066. echo test#24 (try to move parent of current directory)
  1067. mv .. g > mv.log
  1068. rem *** depending on where you are, you either get
  1069. rem *** "sorry, but root directory has no parent" (errorlevel 1) or
  1070. rem *** "not moved to preserve current directory" (errorlevel 0) or
  1071. rem *** "move root directory? You're joking" (errorlevel 1)
  1072. if not exist a goto wrong
  1073. if %2==root if not errorlevel 1 goto noerr
  1074. rem *** if %2==notroot if errorlevel 1 goto err
  1075.  
  1076. rem ** the following tests exercise the "normal" module
  1077.  
  1078. echo test#25 (path includes drive letters but no root)
  1079. mv %1:a %1:h > mv.log
  1080. if errorlevel 1 goto err
  1081. if not exist h goto wrong
  1082. if exist a goto wrong
  1083.  
  1084. echo test#26 (path includes drive letters and root)
  1085. mv %1:h %1:\i > mv.log
  1086. if errorlevel 1 goto err
  1087. if not exist \i goto wrong
  1088. if exist h goto wrong
  1089.  
  1090. echo test#27 (path has upper case letters)
  1091. mv %1:\I A > mv.log
  1092. if errorlevel 1 goto err
  1093. if not exist a goto wrong
  1094. if exist %1:\i goto wrong
  1095.  
  1096. echo test#28 (missing sub-directory name before \..)
  1097. mv a \\.. > mv.log
  1098. if errorlevel 2 goto err
  1099. if not errorlevel 1 goto noerr
  1100. if not exist a goto wrong
  1101.  
  1102. echo test#29 (sub-directory name too long)
  1103. mv a aaaaaaaaaaaaaaaaaaaaa > mv.log
  1104. if errorlevel 2 goto err
  1105. if not errorlevel 1 goto noerr
  1106. if not exist a goto wrong
  1107.  
  1108. echo test#30 (referencing root's parent)
  1109. mv a \.. > mv.log
  1110. if errorlevel 2 goto err
  1111. if not errorlevel 1 goto noerr
  1112. if not exist a goto wrong
  1113.  
  1114. echo test#31 (.. not preceded by \)
  1115. mv a .... > mv.log
  1116. if errorlevel 2 goto err
  1117. if not errorlevel 1 goto noerr
  1118. if not exist a goto wrong
  1119.  
  1120. echo test#32 (source is "*\a\..")
  1121. rem *** this is equal to mv g g which won't be performed
  1122. mv *\a\.. g > mv.log
  1123. if errorlevel 1 goto err
  1124. if not exist a goto wrong
  1125. if not exist g\y.y goto wrong
  1126.  
  1127. echo test#33 (no \ before .)
  1128. mv %1:\g:.\y.y . > mv.log
  1129. if errorlevel 2 goto err
  1130. if not errorlevel 1 goto noerr
  1131. if not exist g\y.y goto wrong
  1132. if exist y.y goto wrong
  1133.  
  1134. echo MV passed all tests! Note the tests in here are not exhaustive.
  1135. echo Now removing test files created awhile ago...
  1136. del g\e\x.x > nul
  1137. rd g\e > nul
  1138. del g\y.y > nul
  1139. rd g > nul
  1140. del a > nul
  1141. del b > nul
  1142. del c > nul
  1143. del d > nul
  1144. del mv.log > nul
  1145. goto dos
  1146.  
  1147. rem *** error handling routines
  1148. :err
  1149. echo MV reported an error that shouldn't happen:
  1150. goto abort
  1151.  
  1152. :noerr
  1153. echo MV failed to detect an error and did this:
  1154. goto abort
  1155.  
  1156. :wrong
  1157. echo MV performed an operation incorrectly:
  1158. goto abort
  1159.  
  1160. :abort
  1161. type mv.log
  1162. echo Please report this problem to the author (type MV without parameters
  1163. echo to get the author's email address).
  1164. goto dos
  1165.  
  1166. :userr
  1167. del a
  1168. echo You have entered the incorrect parameters to testmv.
  1169.  
  1170. :noarg
  1171. echo Usage: testmv [drv] [where]
  1172. echo.
  1173. echo   [drv] is the drive letter of the drive you're running testmv on (e.g
  1174. echo      "A"). No quote, no colon.
  1175. echo.
  1176. echo   [where] is
  1177. echo      "root" if you're running testmv in a root directory
  1178. echo      "notroot" if you're running testmv in a sub-directory
  1179. goto dos
  1180.  
  1181. :dos
  1182.  8-Sep-86 16:13:40-PDT,10218;000000000000
  1183. Return-Path: <pwu@unix.macc.wisc.edu>
  1184. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:11:21 PDT
  1185. Received: by unix.macc.wisc.edu;
  1186.           id AA04984; 4.12/5; Mon, 8 Sep 86 17:32:05 cdt
  1187. Date: Mon, 8 Sep 86 17:32:05 cdt
  1188. From: Peter Wu <pwu@unix.macc.wisc.edu>
  1189. Message-Id: <8609082232.AA04984@unix.macc.wisc.edu>
  1190. To: info-ibmpc-request@mosis
  1191. Subject: normal.c
  1192.  
  1193. /* The normal routine "normalizes" a given path name to this form:
  1194. ** drv:\id\id\id...\id
  1195. ** so that two identical directory/file will yield the same normalized
  1196. ** path regardless of how the user typed it in
  1197. ** E.g.
  1198. **     a:\x\y
  1199. **   and a:\X\Y\.
  1200. **  both yield A:\X\Y
  1201. **
  1202. ** In addition, the later one would also set the flag "dir" indicating
  1203. ** that the user specified it as an directory.
  1204. **
  1205. ** Written by Peter Wu @ Faculty Support Center @ Univ. of Wisconsin
  1206. ** 7/14/86
  1207. */
  1208. #define LINT_ARGS
  1209.  
  1210. #define T_ID1 1  /* id without period */
  1211. #define T_ID2 2  /* id with period */
  1212. #define T_DOT 3  /* '.' */
  1213. #define T_DD  4
  1214. #define T_BS  5
  1215. #define T_COL 6
  1216. #define T_NUL 0
  1217.  
  1218. #define PLEN 200
  1219.  
  1220. #include "dta.h"
  1221. #include <dos.h>
  1222. #include <string.h>
  1223. #include <conio.h>
  1224.  
  1225. char *insert();
  1226.  
  1227. index(str,c) /* first occurence of c in str */
  1228. char *str, c;
  1229. {
  1230.   char *i, d;
  1231.  
  1232.   i = str;
  1233.   d = *i;
  1234.   while (d != '\0') {
  1235.     if (d == c) {
  1236.       return i - str;
  1237.     }
  1238.     i++;
  1239.     d = *i;
  1240.   }
  1241.   return -1;
  1242. }
  1243.  
  1244. char lastc(str)  /* return last character of the string */
  1245. register char *str;
  1246. {
  1247.   if (*str == '\0') {
  1248.     return '\0';
  1249.   }
  1250.  
  1251.   do {
  1252.     str++;
  1253.   } while (*str != '\0');
  1254.  
  1255.   return *(str-1);
  1256. }
  1257.  
  1258. normal(path)  /* normalize a path */
  1259. char *path;
  1260. {
  1261.   char work1[PLEN], work2[PLEN], token[13], token2[13], *ptr;
  1262.   int i, drv, marker, t, ftype, t2;
  1263.  
  1264.   /* first make the path a complete path (include drive, and root) */
  1265.   i = index(path, ':');  /* search for ':' */
  1266.   if (i < 0) {    /* no drive specification */
  1267.     if (*path == '\\') {  /* start from root */
  1268.       work1[0] = '@' + drive();  /* default disk */
  1269.       work1[1] = ':';
  1270.       work1[2] = '\0';
  1271.       strcat(work1, path);  /* now we have full path */
  1272.     } else {  /* no drive and no root */
  1273.       current(0,work1);  /* get current drive and path */
  1274.       strcat(work1, path);  /* now we have full path */
  1275.     }
  1276.   } else {  /* has drive specification */
  1277.     if (path[i+1] != '\\') {  /* but no root, so insert current path */
  1278.       drv = toupper(path[i-1]) - '@';  /* get drive number */
  1279.       current(drv,work1);  /* put down drive and path */
  1280.       strcat(work1, path+i+1);    /* now we have full path */
  1281.     } else {  /* nothing to change */
  1282.       strcpy(work1,path);
  1283.     }
  1284.   }
  1285.  
  1286. #ifdef debug
  1287.   printf("full path is %s\n", work1);
  1288. #endif
  1289.  
  1290.   /* now we have full path in work, but we need to normalize it to get rid
  1291.   ** of .. and .
  1292.   */
  1293.   marker = -2;
  1294.   work2[PLEN-1] = '\0';  /* this is where we accumulate result backwards */
  1295.   ptr = work2 + PLEN - 1;  /* point to first char */
  1296.   ftype = -1;  /* -1=init, 0=unknown,  A_DIR=dir */
  1297.  
  1298.   do {
  1299.     t = scan(work1,&marker,token);
  1300. #ifdef debug
  1301.     printf("ptr is %s so far, and t=%d\n", ptr, t);
  1302. #endif
  1303.     switch (t) {
  1304.  
  1305.       case T_ID1:
  1306.       case T_ID2:
  1307.     ptr = insert(token, ptr);
  1308.     if (ftype == -1) {
  1309.       ftype = 0;  /* this could be a file or a directory */
  1310.     }
  1311.     break;
  1312.  
  1313. #ifdef ha
  1314.       case T_ID2:
  1315.     if (ftype == -1) {
  1316.       ptr = insert(token, ptr);
  1317.       ftype = 0;  /* this could be file or a directory */
  1318.     } else {
  1319.       putn("invalid sub-directory name \"", token, "\"\n\015",0);
  1320.       exit(1);
  1321.     }
  1322.     break;
  1323. #endif
  1324.  
  1325.       case T_DD:
  1326.     t2 = scan(work1,&marker,token);  /* see what's in front of .. */
  1327.     if (t2 != T_BS) {
  1328.       cputs("incorrect use of \"..\" in path name\n\015");
  1329.       exit(1);
  1330.     }
  1331.     /* now delete \id\.. */
  1332.     t2 = scan(work1,&marker,token);  /* read the id (hopefully) */
  1333.     if (t2 != T_ID1) {
  1334.       if (t2 == T_COL) {  /* A:\.. is not permitted */
  1335.         cputs("sorry, but root directory has no parent\n\015");
  1336.         exit(1);
  1337.       } else {  /* what could t2 be? */
  1338.         cputs("missing directory name in front of \"\\..\"\n\015");
  1339.         exit(1);
  1340.       }
  1341.     }
  1342.     t2 = scan(work1,&marker,token2);  /* read the '\' */
  1343.     if (t2 != T_BS) {  /* can this ever happen? */
  1344.       putn("missing \"\\\" before \"", token, "\" in path\n\015",0);
  1345.       exit(1);
  1346.     }
  1347.  
  1348.     /* a:\id\.. should yield a:\ not a:
  1349.     ** a:\id1\..\id2 should yield a:\id2
  1350.     ** this is taken care of somewhere else
  1351.     */
  1352.  
  1353.     /* ok, \id\.. deleted */
  1354.     if (ftype == -1) {
  1355.       ftype = A_DIR;  /* this has to be a directory */
  1356.     }
  1357.     break;
  1358.  
  1359.       case T_DOT:
  1360.     t2 = scan(work1,&marker,token);  /* read the '\' */
  1361.     if (t2 != T_BS) {  /* is this error possible? */
  1362.       cputs("missing \"\\\" in front of \".\"\n\015");
  1363.       exit(1);
  1364.     }
  1365.     if (ftype == -1) {
  1366.       ftype = A_DIR;  /* this has to be a directory */
  1367.     }
  1368.     /* special case is "a:\." we want this to turn into "A:\" and
  1369.     ** not "A:"
  1370.     */
  1371.     break;
  1372.  
  1373.       case T_BS:
  1374.     ptr = insert("\\", ptr);
  1375.     break;
  1376.  
  1377.       case T_COL:
  1378.     ptr = insert(":", ptr);
  1379.     break;
  1380.     }
  1381.   } while (t != T_NUL);
  1382.  
  1383.   if (lastc(ptr) == ':') {  /* special case like a: */
  1384.     strcat(ptr,"\\");
  1385.   }
  1386.   strcpy(path,ptr);  /* copy normalized path back */
  1387.   return ftype;
  1388. }
  1389.  
  1390. char *insert(str1,str2)  /* insert str1 in front of str2 */
  1391. char *str1, *str2;
  1392. {
  1393.   int i;
  1394.  
  1395.   i = strlen(str1);
  1396.   return strncpy(str2 - i, str1, i);
  1397. }
  1398.  
  1399. scan(path,marker,tstr) /* return tokens backwards */
  1400. char *path, *tstr;
  1401. int *marker;
  1402. {
  1403.   char c, d;
  1404.  
  1405.   if (*marker == -2) {    /* signal new scan */
  1406.     *marker = strlen(path) - 1;
  1407.   }
  1408.  
  1409.   if (*marker == -1) {
  1410.     return T_NUL;  /* end of string */
  1411.   }
  1412.  
  1413.   c = path[*marker];
  1414.   if ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ) {
  1415.     return collect_id(tstr, path, marker);
  1416.   }
  1417.  
  1418.   switch (c) {
  1419.     case '.':
  1420.       if (*marker == 0) {
  1421.     *marker = -1;
  1422.     return T_DOT;
  1423.       } else {
  1424.     d = path[*marker - 1];
  1425.     if (d == '.') {
  1426.       *marker -= 2;
  1427.       return T_DD;
  1428.     } else {
  1429.       if ((d == '\\') || (d == ':')) {
  1430.         (*marker)--;
  1431.         return T_DOT;
  1432.       } else {  /* must be an id with trailing dot */
  1433.         return collect_id(tstr, path, marker);
  1434.       }
  1435.     }
  1436.       }
  1437.       cputs("how do I get here?\n\015");
  1438.       error("scan", 0);
  1439.  
  1440.     case '\\':
  1441.       (*marker)--;
  1442.       return T_BS;
  1443.  
  1444.     case ':':
  1445.       (*marker)--;
  1446.       return T_COL;
  1447.  
  1448.     default:
  1449.       return collect_id(tstr, path, marker);  /* non-alpha ID */
  1450.   }
  1451. }
  1452.  
  1453. check_fn(fn)  /* look for invalid characters in file name */
  1454. char *fn;
  1455. {
  1456.   char c, *s;
  1457.  
  1458.   s = fn;
  1459.   while((c= *s) != '\0') {
  1460.     if ((c < 32) || (index("\"[]|<>+=;,/",c) > -1)) {
  1461.       cputs("invalid character '"); putch(c);
  1462.       putn("' in file/sub-directory name \"", fn, "\"", 0);
  1463.       exit(1);
  1464.     }
  1465.     s++;
  1466.   }
  1467. }
  1468.  
  1469. collect_id(tstr, path, marker)
  1470. char *tstr, *path;
  1471. int *marker;
  1472. {
  1473.   char c, id[13];
  1474.   int flag, i;
  1475.  
  1476.   id[13] = '\0';
  1477.   i = 12;
  1478.   flag = T_ID1;  /* no period in file name */
  1479.   do {
  1480.     c = path[*marker];
  1481.  
  1482.     if (c == '.') {  /* indicate there's period in the file name */
  1483.       flag = T_ID2;
  1484.     } else {
  1485.       c = toupper(c);
  1486.       if ((c == '\\') || (c == ':')) {  /* end of file name */
  1487.     strncpy(tstr, id+i+1, 13);
  1488.     check_fn(tstr);
  1489.     return flag;
  1490.       }
  1491.     }
  1492.  
  1493.     id[i] = c;
  1494.     (*marker)--;
  1495.     if (*marker < 0) {
  1496.       strncpy(tstr, id+i, 13);
  1497.       check_fn(tstr);
  1498.       return flag;
  1499.     }
  1500.     i--;
  1501.   } while (i >= 0);
  1502.  
  1503.   putn("file/sub-directory name \"...", id, "\" too long\n\015", 0);
  1504.   exit(1);
  1505. }
  1506.  
  1507. drive()  /* returns current drive number 1=A 2=B */
  1508. {
  1509.   return (char) bdos(0x19, 0, 0) + 1;
  1510. }
  1511.  
  1512. current(drv,cpath)  /* returns current path "drv:\id\..\id on drv */
  1513. char *cpath;
  1514. int drv;
  1515. {
  1516.   union REGS inregs, outregs;
  1517.   char tmp[64];
  1518.  
  1519.   inregs.h.ah = 0x47;  /* get current path */
  1520.   inregs.x.si = (int) tmp;
  1521.   inregs.h.dl = drv;  /* drive number 0=default, 1=A, 2=B */
  1522.   intdos(&inregs, &outregs);
  1523.   if (drv == 0) {
  1524.     drv = drive();
  1525.   }
  1526.   if (outregs.x.cflag) {
  1527.     cputs("cannot find current path on drive "); putch('@'+drv);
  1528.     cputs(":\n\015");
  1529.     error("current", 0);
  1530.   } else {
  1531.     cpath[0] = '@' + drv;
  1532.     cpath[1] = ':';
  1533.     cpath[2] = '\\';
  1534.     cpath[3] = '\0';
  1535.     if (*tmp != '\0') {  /* if not root directory, then append path */
  1536.       strcat(cpath,tmp);
  1537.       strcat(cpath,"\\");
  1538.     }
  1539.   }
  1540. }
  1541.  
  1542. catpath(path, name)  /* concatenate name to path, adding \ when neccessary */
  1543. char *path, *name;
  1544. {
  1545.   if (path[strlen(path)-1] != '\\') {
  1546.     strcat(path, "\\");
  1547.   }
  1548.   strcat(path, name);
  1549. }
  1550.  
  1551. chopath(path)  /* chop off the last portion of a path */
  1552. char *path;
  1553. {
  1554.   char *tmp;
  1555.  
  1556.   tmp = strrchr(path, '\\');
  1557.   if (tmp == (char *)0) {
  1558.     putn("can't find '\\' in ", path, "\n\015");
  1559.     error("chopath", 0);
  1560.   }
  1561.   if (*(tmp-1) == ':') {  /* special case for root */
  1562.     *(tmp+1) = '\0';
  1563.   } else {
  1564.     *tmp = '\0';
  1565.   }
  1566. }
  1567.  
  1568. #define A_INT 0x80    /* interactive flag */
  1569.  
  1570. /* extract the file-type specifier from the given path.
  1571. ** E.g.  x/f becomes x with file type,
  1572. **     animal\dog/d becomes animal\dog with sub-directory type,
  1573. **     *./f becomes *. with file type
  1574. **     *.* becomes *.* with no type
  1575. **     *./i becomes interactive (i.e. prompts for y/n for each file)
  1576. */
  1577. extype(path)
  1578. char *path;
  1579. {
  1580.   char *s;
  1581.   int type;
  1582.  
  1583.   s = strrchr(path,'/');  /* find last '/' */
  1584.   if (s == (char *) 0) {  /* no type specified */
  1585.     return 0;
  1586.   }
  1587.  
  1588.   type = 0;
  1589.   *s = '\0';
  1590.   for (++s; *s != '\0'; s++) {  /* start scanning after the '/' */
  1591.     switch (*s) {
  1592.       case 'f':
  1593.       case 'F':
  1594.     type |= A_FIL;
  1595.     break;
  1596.  
  1597.       case 'd':
  1598.       case 'D':
  1599.     type |= A_DIR;
  1600.     break;
  1601.  
  1602.       case 'h':
  1603.       case 'H':
  1604.     type |= A_HID;    /* search for hidden files */
  1605.     break;
  1606.  
  1607.       case 'i':
  1608.       case 'I':
  1609.     type |= A_INT;    /* requires user's confirmation for each file */
  1610.     break;
  1611.  
  1612.       default:
  1613.     cputs("unrecognized file attribute '"); putch(*s);
  1614.     putn("' in path \"", path, "\"\n\015",
  1615. "valid attributes are f, d, h, i (for file, directory, hidden, interac)\n\015",
  1616.  0);
  1617.     exit(1); /* maybe the user doesn't know what he/she's doing */
  1618.     }
  1619.   }
  1620.   return type;
  1621. }
  1622.  8-Sep-86 16:13:41-PDT,10586;000000000000
  1623. Return-Path: <pwu@unix.macc.wisc.edu>
  1624. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:11:55 PDT
  1625. Received: by unix.macc.wisc.edu;
  1626.           id AA04974; 4.12/5; Mon, 8 Sep 86 17:31:56 cdt
  1627. Date: Mon, 8 Sep 86 17:31:56 cdt
  1628. From: Peter Wu <pwu@unix.macc.wisc.edu>
  1629. Message-Id: <8609082231.AA04974@unix.macc.wisc.edu>
  1630. To: info-ibmpc-request@mosis
  1631. Subject: mv.c
  1632.  
  1633. /* move subdirectories
  1634. ** Written by Peter Wu; July 86.
  1635. ** compile with cc mv /ze
  1636. */
  1637. #define LINT_ARGS
  1638.  
  1639. #define ALLOC 26    /* starting cluster number in directory entry */
  1640. #define PLEN 200    /* max path len */
  1641.  
  1642. #include <dos.h>
  1643. #include "dta.h"
  1644. #include "peek.h"
  1645. #include <conio.h>
  1646.  
  1647. unsigned char func32h();
  1648. unsigned short findir();
  1649. unsigned short clus2sec();
  1650. char *readsec();
  1651. char lastc(char *);
  1652.  
  1653. /* external var */
  1654. extern int
  1655.   num_sec,  /* number of sectors buffered */
  1656.   brk_st;  /* orginal break status */
  1657.  
  1658. /* source & dest must be normalized
  1659. ** mydta1 contains dta of source
  1660. */
  1661. mvdir(mydta1,source,dest)  /* move subdirectory */
  1662. union dtbuf mydta1;
  1663. char *source, *dest;
  1664. {
  1665.   unsigned short sector1, sector2, offset1, offset2, clus1, clus2,
  1666.         d1, d2, status, *p1, *p2, ds1, ds2, bps, parent, drv;
  1667.   char *secbuf1, *secbuf2, *secbuf3, cpath[PLEN];
  1668.   union dtbuf mydta2;
  1669.  
  1670.   /* make sure source is not a root directory */
  1671.   if (lastc(source) == '\\') {
  1672.     cputs("move root directory? You're joking!\n\015");
  1673.     exit(1);
  1674.   }
  1675.  
  1676.   /* make sure source is not predesessor of destination */
  1677.   if (apreb(source, dest)) {
  1678.     /* this will create a directory loop if allowed to go on or
  1679.     ** it's a redundant rename to itself (e.g. mv \ha\. \)
  1680.     */
  1681.     cputs("not moved to avoid loop or redundancy\n\015");
  1682.     return -1;
  1683.   }
  1684.  
  1685.   /* see if source is a predesessor of current directory */
  1686.   drv = toupper(source[0]) - '@';
  1687.   current(drv,cpath);
  1688.   if ( apreb(source,cpath) ) {
  1689.     cputs("not moved to preserve current directory\n\015");
  1690.     return -2;
  1691.   }
  1692.  
  1693.   /* there should be a way to test if source is a predesessor of
  1694.   ** a 'subst' drive's current directory
  1695.   */
  1696.  
  1697.   bset(0);  /* do not allow user to break during this portion */
  1698.  
  1699.   status = mkdir(dest);
  1700.   if (status) {
  1701.     cputs("can't; file exists already/disk write protected\n\015");
  1702.     exit(1);
  1703.   }
  1704.  
  1705.   status = ffmf(dest, A_DIR, &mydta2);
  1706.   if (status) {
  1707.     cputs("error on ffmf after mkdir\n\015");
  1708.     error("mvdir", 0);
  1709.   }
  1710.  
  1711.   num_sec = 0;    /* clear sector buffers */
  1712.  
  1713.   sector1 = findir(mydta1, &offset1, &d1, &secbuf1);
  1714.   if (sector1 < 0) {
  1715.     cputs("cannot find source directory\n\015");
  1716.     error("mvdir", 0);
  1717.   }
  1718.  
  1719.   sector2 = findir(mydta2, &offset2, &d2, &secbuf2);
  1720.   if (sector2 < 0) {
  1721.     cputs("cannot get info on destination directory\n\015");
  1722.     error("mvdir", 0);
  1723.   }
  1724.  
  1725.   if (d1 != d2) {  /* this should have been detected earlier */
  1726.     cputs("source and destination has to be on the same drive\n\015");
  1727.     exit(1);
  1728.   }
  1729.  
  1730.   /* now switch the starting cluster of the two directories */
  1731.   p1 =    (unsigned short *) (secbuf1 + offset1 + ALLOC);
  1732.   p2 =    (unsigned short *) (secbuf2 + offset2 + ALLOC);
  1733.   clus1 = *p1;
  1734.   clus2 = *p2;
  1735. #ifdef debug
  1736.   printf("clus1=%4x  clus2=%4x\n", clus1, clus2);
  1737. #endif
  1738.   *p1 = clus2;
  1739.   *p2 = clus1;
  1740.  
  1741.   status = flirt(d1-1, sector1);  /* mark sector dirty */
  1742.   if (status) {
  1743.     cputs("error in flirt\n\015");
  1744.     error("mvdir", 0);
  1745.   }
  1746.  
  1747.   status = flirt(d1-1, sector2);  /* mark sectors as dirty */
  1748.   if (status) {
  1749.     cputs("error in flirt\n\015");
  1750.     error("mvdir", 0);
  1751.   }
  1752.  
  1753.   /* now we must find the cluster# of the parent directory of the
  1754.   ** destination directory (slot number two in clus2) so we can put
  1755.   ** it in the source directory (which will become the new destination
  1756.   ** directory.
  1757.   */
  1758.   ds1 = clus2sec(d1, clus1, &bps);  /* sector# of cluster 1 */
  1759.   ds2 = clus2sec(d1, clus2, &bps);  /* sector# of cluster 2 */
  1760.   secbuf3 = readsec(d1-1,ds2);
  1761.   if (secbuf3 == (char *) 0) {
  1762.     cputs("error in calling readsec\n\015");
  1763.     error("mvdir", 0);
  1764.   }
  1765.  
  1766.   parent = * (unsigned short *) (secbuf3 + 32 + ALLOC);
  1767. #ifdef debug
  1768.   printf("parent is %4x\n", parent );
  1769. #endif
  1770.  
  1771.   /* now write this into source dir */
  1772.  
  1773.   secbuf3 = readsec(d1-1,ds1);
  1774.   if (secbuf3 == (char *) 0) {
  1775.     cputs("error in calling readsec\n\015");
  1776.     error("mvdir", 0);
  1777.   }
  1778.  
  1779.   *(unsigned short *) (secbuf3 + 32 + ALLOC) = parent;
  1780.   status = flirt(d1-1,ds1);
  1781.   if (status) {
  1782.     cputs("error in flirt\n\015");
  1783.     error("mvdir", 0);
  1784.   }
  1785.  
  1786.   /* now write back all three (or less) modified sectors */
  1787.   status = writesec(d1-1,ds1);    /* the cluster containing parent pointer */
  1788.   if (status) {  /* what could cause this to happen? */
  1789.     cputs("error in writing first modified sector\n\015");
  1790.     error("mvdir", 1);
  1791.   }
  1792.  
  1793.   status = writesec(d1-1,sector2);
  1794.   if (status) {  /* it's impossible for this to happen */
  1795.     cputs("error in writing second modified sector\n\015");
  1796.     error("mvdir", 1);
  1797.   }
  1798.  
  1799.   status = writesec(d1-1,sector1);
  1800.   if (status) {  /* this is also impossible */
  1801.     cputs("error in writing third modified sector\n\015");
  1802.     error("mvdir", 1);
  1803.   }
  1804.  
  1805.   bdos(0xd,0,0);  /* reset disk - neccessary for rmdir to work since DOS'
  1806.           ** buffer now contains invalid information (it didn't
  1807.           ** know that the disk was modified).
  1808.           */
  1809.   status = rmdir(source);  /* this dir should be empty now */
  1810.   if (status) {
  1811.     putn(
  1812. "Oops!\n\015",
  1813. "cannot remove old directory \"", source, "\"\n\015",
  1814. "Maybe one of your `subst' disk is using this directory. If this is\n\015",
  1815. "the case, remove the subst disk and then remove this directory.\n\015", 0);
  1816.     exit(1);
  1817.   }
  1818.  
  1819.   bset(brk_st);  /* restore break status */
  1820.   return 0;  /* no error */
  1821. }
  1822.  
  1823. unsigned short findir(mydta, offsetp, drv, secbuf)
  1824. char **secbuf;
  1825. unsigned short *offsetp;
  1826. unsigned int *drv;
  1827. union dtbuf mydta;
  1828. {
  1829.   unsigned int status, i, j, offset, sector, cluster, tmp, bps;
  1830.   union REGS inregs, outregs;
  1831.   struct SREGS segregs;
  1832.   char c, dir[13];  /* directory entry formatted like X.Y not "X       Y  " */
  1833.  
  1834.   if (mydta.dos.attr != A_DIR) {  /* guard against programmer's error */
  1835.     putn(mydta.dos.fn, " is not a directory\n\015",0);
  1836.     error("findir", 0);
  1837.   }
  1838.  
  1839.   /* now find the cluster where the searched directory is */
  1840.   cluster = mydta.dos.clusl + (mydta.dos.clush << 8);
  1841.  
  1842.   /* now convert the cluster number to sector number */
  1843.   sector = clus2sec(mydta.dos.drv_no, cluster, &bps);
  1844.  
  1845.   /* calculate directory entry offset in the sector */
  1846.   offset = ((mydta.dos.sloth << 8) + mydta.dos.slotl) * 32;
  1847.  
  1848.   sector += offset / bps;  /* normalize sector & offset */
  1849.   offset %= bps;
  1850.  
  1851. #ifdef debug
  1852.   printf("dir at sector %d\n", sector);
  1853. #endif
  1854.  
  1855.   *secbuf = readsec(mydta.dos.drv_no - 1, sector);
  1856.   if (*secbuf == (char *) 0) {
  1857.     cputs("error in calling readsec\n\015");
  1858.     error("findir", 0);
  1859.   }
  1860.  
  1861. #ifdef debug
  1862.   printf("First 11 bytes in sector: %11.11s\n", *secbuf+offset);
  1863.   printf("Fn returned by ffmf: %11.11s\n", mydta.fn);
  1864. #endif
  1865.  
  1866.   /* redundant check to see if we have the correct directory entry */
  1867.   /* first format the directory entry name in this form "*.*" instead of
  1868.   ** "???????????"
  1869.   */
  1870.   i=0;
  1871.   c = (*secbuf)[offset];
  1872.   while ((c != ' ') && (i < 8)) {  /* copy the first 8 characters */
  1873.     dir[i] = c;
  1874.     i++;
  1875.     c = (*secbuf)[offset+i];
  1876.   }
  1877.  
  1878.   j = 8;
  1879.   c = (*secbuf)[offset+j];
  1880.   if (c != ' ') {  /* sub-dir name has extension */
  1881.     /* now add '.' and copy the extension */
  1882.     dir[i] = '.';
  1883.     i++;
  1884.     while ((c != ' ') && (j < 11)) {
  1885.       dir[i] = c;
  1886.       i++;
  1887.       j++;
  1888.       c = (*secbuf)[offset + j];
  1889.     }
  1890.   }
  1891.   dir[i] = '\0';  /* terminate string */
  1892. #ifdef debug
  1893.   printf("formatted directory entry string: %s\n", dir);
  1894. #endif
  1895.  
  1896.   if (strcmp(mydta.dos.fn, dir) == 0) {
  1897. #ifdef debug
  1898.     printf("Directory entry found!\n");
  1899. #endif
  1900.   } else {
  1901.     cputs("cannot find directory entry\n\015");
  1902.     error("findir", 0);
  1903.   }
  1904.  
  1905.   /* redundant check to make sure file size of directory is zero */
  1906.   if (*(unsigned long *)(*secbuf+offset+28) != 0L) {
  1907.     cputs("found directory with size > 0\n\015");
  1908.     error("findir", 0);
  1909.   }
  1910.  
  1911.   *offsetp = offset;
  1912.   *drv = mydta.dos.drv_no;
  1913.   return sector;
  1914. }
  1915.  
  1916. unsigned short clus2sec(drv, clus_no, pbps)  /* convert cluster to sector */
  1917. unsigned short clus_no, *pbps, drv;
  1918. {
  1919.   unsigned char status;
  1920.   static unsigned short spc=0, ss, bps, tabseg, taboff;
  1921.   unsigned short sector;
  1922.  
  1923.   if (spc == 0) {  /* first time function is called */
  1924.     status = func32h(drv, &tabseg, &taboff);  /* See PC Tech Journal */
  1925.     if (status == 0xff) {
  1926.       cputs("func32h: invalid drive: "); putch(drv+'A'); cputs("\n\015");
  1927.       error("clus2sec", 0);
  1928.     }
  1929.  
  1930.     spc = peekb(tabseg,taboff+4)+1;  /* sector per cluster */
  1931.     ss =  peekw(tabseg,taboff+11);  /* starting data sector */
  1932.     bps = peekw(tabseg,taboff+2);  /* bytes per sector */
  1933.  
  1934. #ifdef debug
  1935.     printf("drive #: %d\n", drv);
  1936.     printf("sectors per cluster: %d\n", spc);
  1937.     printf("# allocation units: %d\n",  peekw(tabseg,taboff+13)-1 );
  1938.     printf("sector size: %d\n", bps);
  1939.     printf("starting sector: %d\n", ss);
  1940. #endif
  1941.   }
  1942.   *pbps = bps;    /* return this value */
  1943.  
  1944.   if (clus_no == 0) {
  1945. #ifdef debug
  1946.     printf("parent directory is root! - special case\n");
  1947. #endif
  1948.     sector = peekw(tabseg,taboff+16);  /* first sector of root directory */
  1949.   } else {
  1950.     sector = (clus_no - 2) * spc + ss;    /* see DOS Tech. Ref */
  1951.   }
  1952.  
  1953.   return sector;
  1954. }
  1955.  
  1956. /* apreb test to see if patha is equal to or is a predessesor of pathb
  1957. ** This is used to test whether moving a directory would result
  1958. ** in a directory loop condition and also whether the source
  1959. ** directory is a predessor of the current path (can't delete
  1960. ** source directory in this case, so don't move)
  1961. **
  1962. ** patha and pathb must be normalized paths
  1963. ** E.g.
  1964. **       apreb("A:\DEF", "A:\DEF")    is true
  1965. **       apreb("A:\DEF", "A:\DEF\GHI")   is also true
  1966. **       apreb("A:\DEF", "A:\DEFG")    is false
  1967. */
  1968. apreb(patha, pathb)
  1969. char *patha, *pathb;
  1970. {
  1971.   int i, lena, lenb;
  1972.   char c;
  1973.  
  1974.   lena = strlen(patha);
  1975.   lenb = strlen(pathb);
  1976.   if (lena > lenb) {  /* if patha is longer, it can't be a predessesor */
  1977.     return 0;
  1978.   }
  1979.  
  1980.   c = pathb[lena];  /* if patha is a predessesor, c should be '\\' or '\0' */
  1981.   if ((c != '\\') && (c != '\0')) {
  1982.     return 0;
  1983.   }
  1984.  
  1985.   if (strncmp(patha, pathb, lena)) {  /* not equal */
  1986.     return 0;
  1987.   }
  1988.  
  1989.   return 1;
  1990. }
  1991.  8-Sep-86 16:13:42-PDT,14202;000000000000
  1992. Return-Path: <pwu@unix.macc.wisc.edu>
  1993. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:12:30 PDT
  1994. Received: by unix.macc.wisc.edu;
  1995.           id AA04958; 4.12/5; Mon, 8 Sep 86 17:31:19 cdt
  1996. Date: Mon, 8 Sep 86 17:31:19 cdt
  1997. From: Peter Wu <pwu@unix.macc.wisc.edu>
  1998. Message-Id: <8609082231.AA04958@unix.macc.wisc.edu>
  1999. To: info-ibmpc-request@mosis
  2000. Subject: front.c
  2001.  
  2002. /* Program to move files and/or subdirectories - like mv on unix
  2003. ** Written by Peter Wu July, 86 @ Faculty Support Center @ UW-Madison
  2004. ** Compile with IBM C version 1.00 (or Microsoft C 3.00).
  2005. ** Link with fstat, mv, normal, sector, absdr, func32h, break, putn /stack:5000
  2006. **
  2007. ** This is module "front.c", the front end of mv. It looks at the sources
  2008. ** and destination supplied by the user and determines whether to call
  2009. ** mvdir to move/rename sub-directories or call rename to move/rename files.
  2010. */
  2011.  
  2012. /* programmer's notes:
  2013. -- won't work on network disk
  2014. --
  2015. -- things to work on:
  2016. --
  2017. -- be interactive:
  2018. --   ask if user wants to replace existing file/directory
  2019. --   verbose mode where user must confirm each move
  2020. -- better error handling
  2021. -- make it work on network disks
  2022. -- detect and report write-protected disk if possible
  2023. -- allow /f option on destination?
  2024. --
  2025. -- bugs:
  2026. +--------------------------------+
  2027. |  c:                 |
  2028. |  subst p: /user/peter/mv/test  |
  2029. |  mv /usr/peter/mv/test .     |
  2030. +--------------------------------+
  2031. -- The above sequence will prevent mv from removing the source directory after
  2032. -- the content is moved to the destination directory. I don't know any way to
  2033. -- detect this BEFORE moving the content. So my solution is to print a message
  2034. -- telling the user to remove the source directory (now empty) by hand after
  2035. -- he got rid of the 'subst drive'.
  2036. */
  2037. #define LINT_ARGS
  2038.  
  2039. #define UNK_TYP 0
  2040.  
  2041. #define A_INT 0x80  /* interactive file attribute */
  2042.  
  2043. #define LASTDRV 26  /* drive# larger than this is network share disk */
  2044. #define NULL (char *) 0
  2045. #define PLEN 200    /* max. path length; hope it's long enough */
  2046.  
  2047. #include "dta.h"
  2048. #include "date.h"
  2049. #include <conio.h>
  2050. #include <string.h>
  2051. #include <stdlib.h>
  2052.  
  2053. char lastc(char *);
  2054. char getkey(char *);
  2055.  
  2056. int brk_st;  /* original break status: 0=break off, 1=break on */
  2057.  
  2058.   /* When break is on, user can break the program when any DOS function is
  2059.   ** called. When break is off, user can only break the program when a DOS
  2060.   ** I/O function is called (if the user presses break before an I/O function,
  2061.   ** DOS will remember the break but let the program keep running until the
  2062.   ** program calls an IO function). It is undesirable to have break on
  2063.   ** all the time since for instance if the user breaks the program after I
  2064.   ** wrote one sector to disk (I need to write three sectors per sub-directory
  2065.   ** moved), he'll end up with an inconsistent directory. The solution is to
  2066.   ** make sure break is off when I'm doing disk writes, and restore it
  2067.   ** to its orginal status when I'm done writing the disk.
  2068.   ** If the user really wants to mess up his disk, he can still do it with
  2069.   ** ctrl-alt-del or switching the power off in the middle of running mv.
  2070.   */
  2071.  
  2072. main(argc,argv)
  2073. int argc;
  2074. char *argv[];
  2075. {
  2076.   unsigned int status, dtype, stype, status2, smask, dmask, i, drv, plimit,
  2077.            doit;
  2078.   union dtbuf mydta1, mydta2, tmpdta;
  2079.   char fsource[PLEN], fdest[PLEN], *source, *dest, *tmp, fdest2[PLEN];
  2080.  
  2081.   if (argc < 3) {  /* print help */
  2082.     putn(
  2083.  
  2084. "Usage: MV <source1> <source2> .. <sourceN> <dest>\n\15",
  2085. "  MV renames/moves the source files/directories to the destination.\n\15",
  2086. "  Wildcards ok. Specify source type with /d, /f, /h, and /i.\n\15",
  2087. "  /d=sub-directories, /f=files, /h=search hidden, /i=interactive.\n\15",
  2088. "    \"*.*\\.\" or \"*.*/d\" specifies all sub-directories only\n\15",
  2089. "    \"*.*/f\" specifies all visible files only\n\15",
  2090. "    \"*.*/hf\" specifies all visible and hidden files\n\15",
  2091. "    \"*.*/fi\" will prompt you (move or not) for every file found\n\15",
  2092. "  Version 1.20 made ", date, ". For DOS 2.xx and 3.xx.\n\n\15",
  2093. "Please send comments/bug reports to one of the following addresses:\n\15",
  2094. "  Arpanet: pwu@unix.macc.wisc.edu\n\15",
  2095. "  Bitnet: WU at WISVMACC\n\15",
  2096. "  CompuServe: 76377,1332\n\15",
  2097. "  UUCP: {akgua|ihnp4|seismo|harvard|allegra|ucbvax}!uwvax!uwmacc!pwu\n\15",
  2098. NULL);
  2099.  
  2100.     exit(0);
  2101.   }
  2102.  
  2103.   /* test DOS version */
  2104.   switch (_osmajor) {
  2105.     case 2:  /* dos 2.xx */
  2106.       break;
  2107.     case 3:  /* dos 3.xx */
  2108.       if (_osminor <= 20) {  /* make sure it's no later than version 3.20 */
  2109.     break;
  2110.       }
  2111.     default:
  2112.       cputs("need DOS between version 2.00 and 3.20\n\15");
  2113.       exit(1);
  2114.   }
  2115.  
  2116.   plimit = PLEN - 80;  /* limit on user supplied path name */
  2117.  
  2118.   /* process destination first */
  2119.   dest = argv[argc-1];
  2120.   if (strlen(dest) > plimit) {
  2121.     cputs("destination path too long\n\15");
  2122.     exit(1);
  2123.   }
  2124.  
  2125.   strcpy(fdest,dest);
  2126.   dtype = normal(fdest);  /* normalize destination path */
  2127.  
  2128.   if (dtype == 0) {  /* no specified type for destination */
  2129.     dmask = A_FIL | A_DIR;
  2130.   } else {
  2131.     dmask = dtype;
  2132.   }
  2133.  
  2134.   status2 = ffmf(fdest, A_MASK, &mydta2);  /* find info on dest */
  2135. #ifdef debug
  2136.   printf("fdest is %s, dmask=%d, status2 = %d\n", fdest, dmask, status2);
  2137. #endif
  2138.  
  2139.   if (!status2) {  /* if destination exists  */
  2140. #ifdef debug
  2141.     printf("destination exists and has attr: %x\n", mydta2.dos.attr);
  2142. #endif
  2143.     /* check if destination is ambigious here */
  2144.     if (lastc(fdest) != '\\') { /* root would cause error, so don't check it */
  2145.       tmpdta = mydta2;    /* don't disturb mydta2, we need it later */
  2146.       status = fnmf(&tmpdta);
  2147.       if (!status) {  /* ha, there's more than one destination! */
  2148.     cputs("destination is ambigious\n\15");
  2149.     exit(1);
  2150.       }
  2151.     }
  2152.  
  2153.     /* if destination is an existing file, report error */
  2154.     if ((mydta2.dos.attr & A_DIR) == 0) {
  2155.       cputs("destination is an existing file!\n\15");
  2156.       exit(1);
  2157.     }
  2158.  
  2159.     /* fix fdest so that a destination of "*\." will have the expanded name
  2160.     ** by removing the * and appending the directory name found by ffmf.
  2161.     ** This will cause an error if fdest is the root of a 'subst' disk,
  2162.     ** so we must make sure fdest is not a root.
  2163.     */
  2164.     if (lastc(fdest) != '\\') {  /* if not root then */
  2165.       tmp = strrchr(fdest,'\\');  /* find last '\' */
  2166.       if (tmp == NULL) {  /* no '\' ????!!!! */
  2167.     cputs("error after strrchr: cannot find \\\n\15");
  2168.     error("front",0);
  2169.       }
  2170.       strcpy(tmp+1, mydta2.dos.fn);  /* dest with wild cards expanded */
  2171.     }
  2172.  
  2173.   } else {  /* destination not found */
  2174.  
  2175. #ifdef debug
  2176.     printf("destination not exists\n");
  2177. #endif
  2178.  
  2179.     if (dtype == A_DIR) {  /* specified directory not exist */
  2180.       cputs("can't find destination directory\n\15");
  2181.       exit(1);
  2182.     } else {  /* destination type not specified or of file type */
  2183.       /* check: source better be unambigious */
  2184.       if (argc > 3) {
  2185.     cputs("can't rename more than one source\n\15");
  2186.     exit(1);
  2187.       }
  2188.       /* destination is not found, let's lookup destination's parent.
  2189.       ** This has to exist (e.g. if C:\X\Y is destination and doesn't
  2190.       ** exist, C:\X should still exists). This lookup also allows us
  2191.       ** to compare the drive number between the destination and the
  2192.       ** source to detect cross device move.
  2193.       */
  2194.       strcpy(fdest2, fdest);
  2195.       chopath(fdest2);    /* remove last portion of path */
  2196.       status = ffmf(fdest2, A_MASK, &mydta2);
  2197.       if (status) {  /* if destination's parent doesn't exist */
  2198.     putn("path \"", fdest2, "\" not exist!\n\15", 0);
  2199.     exit(1);
  2200.       } else if ((mydta2.dos.attr & A_DIR) == 0) { /* not a directory */
  2201.     putn("\"", fdest2, "\" is not a directory!\n\15", 0);
  2202.     exit(1);
  2203.       }
  2204.     }
  2205.   }
  2206.  
  2207.   brk_st = bstat();  /* get current break status (on or off) */
  2208.  
  2209.   /***********************************************************/
  2210.   /* now process SOURCE **************************************/
  2211.   /***********************************************************/
  2212.   for (i=1; i < argc - 1; i++) {  /* for all source specification */
  2213.     source = argv[i];
  2214.     if (strlen(source) > plimit) {  /* rare user error but just in case */
  2215.       cputs("source path too long! skipped\n\15");
  2216.       continue;  /* next argument */
  2217.     }
  2218.  
  2219.     strcpy(fsource,source);
  2220.     stype = extype(fsource);  /* extract type */
  2221.     stype |= normal(fsource);  /* normalize source path */
  2222.  
  2223. #ifdef debug
  2224.     printf("normalized source path: %s\n", fsource);
  2225. #endif
  2226.  
  2227.     if ((stype & (A_DIR | A_FIL)) == 0) {  /* no specified type, use default */
  2228.       smask = stype | A_FIL | A_DIR;  /* look for file or directory */
  2229.     } else {
  2230.       smask = stype;
  2231.     }
  2232.  
  2233.     status = ffmf(fsource, smask, &mydta1);  /* find info on source */
  2234.     if (status) {
  2235.       putn(source, " not found\n\15", 0);
  2236.       continue;
  2237.     }
  2238.  
  2239.     /* see if source is on a network disk */
  2240.     if ((mydta1.dos.drv_no < 0) || (mydta1.dos.drv_no >= LASTDRV)) {
  2241.       cputs("sorry, mv doesn't work on network disks\n\15");
  2242.       exit(1);
  2243.     }
  2244.  
  2245.     /* is source and destination on same drive? */
  2246.     if (mydta1.dos.drv_no != mydta2.dos.drv_no) {
  2247.       cputs("source and destination must be on same physical drive\n\15");
  2248.       exit(1);
  2249.     }
  2250.  
  2251.     /* check: if destination does not exist, source better be unique */
  2252.     if (status2) {  /* destination does not exist */
  2253.       if (lastc(fsource) != '\\') { /* don't call fnmf on root */
  2254.     tmpdta = mydta1;  /* do not change mydta1, mess with a copy */
  2255.     status = fnmf(&tmpdta);  /* see if there's more than one source */
  2256.     if (!status) {    /* source is ambigious */
  2257.       cputs("can't rename more than one source\n\15");
  2258.       exit(1);
  2259.     }
  2260.       }
  2261.     }
  2262.  
  2263.     do {  /* repeat for all source wildcard */
  2264.  
  2265. #ifdef debug
  2266.       printf("source found, fn: %s\n", mydta1.dos.fn);
  2267.       printf("found attribute is %2xh\n", mydta1.dos.attr);
  2268. #endif
  2269.  
  2270.       /* form source name with wild cards expanded */
  2271.       chopath(fsource);  /* chop off wild cards */
  2272.       catpath(fsource, mydta1.dos.fn);    /* append expanded name */
  2273.  
  2274.       if (status2) {  /* destination not exist */
  2275.  
  2276.     if (stype & A_INT) {  /* interactive mode */
  2277.       doit = prompt(fsource,fdest);
  2278.     } else {  /* in batch mode, always do it */
  2279.       putn(fsource, " DD ", fdest, "  ", 0);
  2280.       doit = 1;  /* do it! */
  2281.     }
  2282.  
  2283.     if (doit) {
  2284.  
  2285.       if (mydta1.dos.attr & A_DIR) {  /* rename or move directory */
  2286.  
  2287.         status =  mvdir(mydta1, fsource, fdest);
  2288.         if (!status) {
  2289.           cputs("OK.\n\15");
  2290.         }
  2291.  
  2292.       } else {  /* rename or move file */
  2293.  
  2294.         /* don't know what rename does when user presses break key,
  2295.         ** so set break off to be safe
  2296.         */
  2297.         bset(0);  /* turn break off (disable break key) */
  2298.         status = rename(fdest,fsource);  /* use fdest or dest? */
  2299.         bset(brk_st);  /* restore break status to orginal value */
  2300.  
  2301.         if (status) {
  2302.           putn("can't\n\15",
  2303.            "invalid file name/disk write protected\n\15", 0);
  2304.         } else {
  2305.           cputs("ok\n\15");
  2306.         }
  2307.         /* fall thru to fnmf */
  2308.       }
  2309.  
  2310.     } else {  /* doit = 0, because the user press 'n' */
  2311.  
  2312.       cputs("not moved\15\n");
  2313.  
  2314.     }
  2315.  
  2316.       } else {    /* now handle the case when destination do exists */
  2317.  
  2318. #ifdef debug
  2319.     printf("dest exists, let's see...\n");
  2320. #endif
  2321.  
  2322.     if (mydta2.dos.attr & A_DIR) {    /* destination is a directory */
  2323. #ifdef debug
  2324.       printf("before strcat fdest is %s\n", fdest);
  2325.       printf("now cat with %13.13s\n", mydta1.dos.fn);
  2326. #endif
  2327.  
  2328.       /* create destination + tail of source in fdest2
  2329.       ** e.g.      source = \user\peter\cat
  2330.       **      destination = \junk\haha
  2331.       **           fdest2 = \junk\haha\cat
  2332.       */
  2333.       strcpy(fdest2, fdest);  /* make a work copy */
  2334.       catpath(fdest2, mydta1.dos.fn);
  2335.  
  2336.       if (stype & A_INT) {    /* interactive mode */
  2337.         doit = prompt(fsource,fdest2);
  2338.       } else {  /* in batch mode, always do it */
  2339.         putn(fsource, " DD ", fdest2, "  ", 0);
  2340.         doit = 1;  /* do it! */
  2341.       }
  2342.  
  2343.       if (doit) {
  2344.  
  2345. #ifdef debug
  2346.         printf("dest+tail of source: %s\n", fdest2);
  2347. #endif
  2348.  
  2349.         if (mydta1.dos.attr & A_DIR) {  /* source is also a directory */
  2350.  
  2351.           status = mvdir(mydta1, fsource, fdest2);
  2352.           if (!status) {
  2353.         cputs("OK.\n\15");
  2354.           }
  2355.           /* fall down to fnmf */
  2356.  
  2357.         } else {  /* move a file into a directory */
  2358.  
  2359.           bset(0);    /* turn break off (disable break key) */
  2360.           status = rename(fdest2, fsource);
  2361.           bset(brk_st);  /* set break status to orginal value */
  2362.  
  2363.           if (status) {
  2364.         cputs("can't; file exists already/disk write-protected\n\15");
  2365.           } else {
  2366.         cputs("ok.\n\15");
  2367.           }
  2368.         }
  2369.       } else {  /* doit = 0 */
  2370.         cputs("not moved\15\n");
  2371.       }
  2372.  
  2373.     } else {  /* dest is an existing file! */
  2374.  
  2375.       putn(fsource, " DD ", fdest,
  2376.            "   can't rename: file exists already\n\15", 0);
  2377.  
  2378.     }
  2379.  
  2380.       }  /* if dest exist or not */
  2381.  
  2382.       status = fnmf(&mydta1);  /* find info on next matching source */
  2383.  
  2384.     } while (status == 0);  /* while there are matching files */
  2385.   }  /* for different sources */
  2386.  
  2387.   exit(0);  /* no error */
  2388. }
  2389.  
  2390. prompt(s,d)  /* return 1 for 'y'; 0 for 'n' */
  2391. char *s, *d; /* part of the prompt */
  2392. {
  2393.   char c;
  2394.  
  2395.   do {
  2396.     putn(s, " DD ", d, " (y/n/q; ?=help): ", 0);
  2397.     c = getkey("yYnNqQ?");
  2398.     putch(' ');
  2399.     switch (c) {
  2400.  
  2401.       case '?':
  2402.     putn("\15\n",
  2403.          "y - yes, move it\15\n",
  2404.          "n - no, skip it\15\n",
  2405.          "q - quit to DOS\15\n",
  2406.          "? - this help message\15\n\n",
  2407.          0);
  2408.     break;
  2409.  
  2410.       case 'y':
  2411.       case 'Y':
  2412.     return 1;
  2413.  
  2414.       case 'n':
  2415.       case 'N':
  2416.     return 0;
  2417.  
  2418.       case 'q':
  2419.       case 'Q':
  2420.     cputs("Quit.\15\n");
  2421.     exit(0);
  2422.  
  2423.       default:
  2424.     cputs("\15\nHow do I get here?\15\n");
  2425.     error("prompt",0);
  2426.  
  2427.     }  /* end switch */
  2428.   } while (1);
  2429. }
  2430.  
  2431. char getkey(valid)  /* wait for valid key and echo it */
  2432. char *valid;
  2433. {
  2434.   char c;
  2435.   int i;
  2436.  
  2437.   do {
  2438.     c = getch();
  2439.     i = index(valid,c);
  2440.     if (i > -1) {
  2441.       putch(c);  /* echo valid key */
  2442.       return c;
  2443.     } else {  /* beep at invalid key */
  2444.       putch(7);
  2445.     }
  2446.   } while (1);
  2447. }
  2448.  8-Sep-86 16:23:29-PDT,962;000000000000
  2449. Return-Path: <pwu@unix.macc.wisc.edu>
  2450. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:18:48 PDT
  2451. Received: by unix.macc.wisc.edu;
  2452.           id AA04970; 4.12/5; Mon, 8 Sep 86 17:31:37 cdt
  2453. Date: Mon, 8 Sep 86 17:31:37 cdt
  2454. From: Peter Wu <pwu@unix.macc.wisc.edu>
  2455. Message-Id: <8609082231.AA04970@unix.macc.wisc.edu>
  2456. To: info-ibmpc-request@mosis
  2457. Subject: mv
  2458.  
  2459. front.obj: front.c date.h dta.h
  2460.     cc front /OT;
  2461.  
  2462. mv.obj: mv.c dta.h peek.h
  2463.     cc mv /ze /OT;
  2464.  
  2465. fstat.obj: fstat.c dta.h
  2466.     cc fstat /OT;
  2467.  
  2468. normal.obj: normal.c dta.h
  2469.     cc normal /OT;
  2470.  
  2471. sector.obj: sector.c
  2472.     cc sector /OT;
  2473.  
  2474. break.obj: break.c
  2475.     cc break /OT;
  2476.  
  2477. error.obj: error.c
  2478.     cc error;
  2479.  
  2480. func32h.obj: func32h.asm
  2481.     masm func32h;
  2482.  
  2483. absdr.obj: absdr.asm
  2484.     masm absdr;
  2485.  
  2486. putn.obj: putn.asm
  2487.     masm putn;
  2488.  
  2489. mv.exe: mv.obj fstat.obj normal.obj front.c sector.obj func32h.obj absdr.obj
  2490.  clink mv fstat normal front sector break func32h absdr putn error /stack:5000;
  2491.  8-Sep-86 16:23:33-PDT,1009;000000000000
  2492. Return-Path: <pwu@unix.macc.wisc.edu>
  2493. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:19:01 PDT
  2494. Received: by unix.macc.wisc.edu;
  2495.           id AA04924; 4.12/5; Mon, 8 Sep 86 17:30:36 cdt
  2496. Date: Mon, 8 Sep 86 17:30:36 cdt
  2497. From: Peter Wu <pwu@unix.macc.wisc.edu>
  2498. Message-Id: <8609082230.AA04924@unix.macc.wisc.edu>
  2499. To: info-ibmpc-request@mosis
  2500. Subject: break.c
  2501.  
  2502. /* routines to read break status and set break status (to on or off) */
  2503.  
  2504. #include <dos.h>
  2505.  
  2506. bstat()  /* get break status 0=off, 1=on */
  2507. {
  2508.   union REGS inregs, outregs;
  2509.  
  2510.   inregs.h.al = 0;  /* request break status */
  2511.   inregs.h.ah = 0x33;  /* DOS function for ctrl-break check */
  2512.   intdos(&inregs, &outregs);
  2513.   return outregs.h.dl;
  2514. }
  2515.  
  2516. bset(onoff)  /* set break status 0=off 1=on */
  2517. {
  2518.   union REGS inregs, outregs;
  2519.  
  2520.   inregs.h.al = 1;  /* set break status */
  2521.   inregs.h.ah = 0x33;  /* DOS function for ctrl-break check */
  2522.   inregs.h.dl = onoff;    /* set to on or off */
  2523.   intdos(&inregs, &outregs);
  2524. }
  2525.  8-Sep-86 16:23:36-PDT,1153;000000000000
  2526. Return-Path: <pwu@unix.macc.wisc.edu>
  2527. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:19:27 PDT
  2528. Received: by unix.macc.wisc.edu;
  2529.           id AA04992; 4.12/5; Mon, 8 Sep 86 17:32:20 cdt
  2530. Date: Mon, 8 Sep 86 17:32:20 cdt
  2531. From: Peter Wu <pwu@unix.macc.wisc.edu>
  2532. Message-Id: <8609082232.AA04992@unix.macc.wisc.edu>
  2533. To: info-ibmpc-request@mosis
  2534. Subject: putn.asm
  2535.  
  2536. ; allows C program to call cputs with multiple arguments.
  2537. ; E.g. putn("How ", "are ", "you?", 0);
  2538. ; the last argument must be either 0 or ""
  2539.  
  2540. _text    segment public byte 'code'
  2541.     assume cs:_text
  2542.  
  2543.     extrn    _cputs:near
  2544.  
  2545.     public    _putn
  2546. _putn    proc    near
  2547.     push    si
  2548.     push    di
  2549.     push    bp
  2550.     mov    bp,sp
  2551.  
  2552.     mov    si,8
  2553. loop:
  2554.     mov    di,[bp+si]    ; get next string
  2555.     or    di,di        ; is it NULL
  2556.     je    done        ; if so, nothing more to print
  2557.     or    byte ptr [di],0 ; it is "" (null string)
  2558.     je    done        ; if so, nothing more to print
  2559.  
  2560. ; now call cputs to print the string
  2561.     push    di        ; push parameter on stack
  2562.     call    _cputs
  2563.     pop    di        ; get rid of parameter on stack
  2564.  
  2565.     add    si,2        ; point to next parameter
  2566.     jmp    loop
  2567.  
  2568. done:
  2569.     pop    bp
  2570.     pop    di
  2571.     pop    si
  2572.     ret
  2573. _putn    endp
  2574. _text    ends
  2575.     end
  2576.  8-Sep-86 16:23:47-PDT,1765;000000000000
  2577. Return-Path: <pwu@unix.macc.wisc.edu>
  2578. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:19:46 PDT
  2579. Received: by unix.macc.wisc.edu;
  2580.           id AA04919; 4.12/5; Mon, 8 Sep 86 17:30:29 cdt
  2581. Date: Mon, 8 Sep 86 17:30:29 cdt
  2582. From: Peter Wu <pwu@unix.macc.wisc.edu>
  2583. Message-Id: <8609082230.AA04919@unix.macc.wisc.edu>
  2584. To: info-ibmpc-request@mosis
  2585. Subject: absdr.asm
  2586.  
  2587. ; bios call to do absolute disk read/write
  2588. ; using int86 from C to call int 25h and 26h will freeze the computer
  2589. ; (because si and di were modified?), so I had to write this in assembly.
  2590.  
  2591. _text    segment public byte 'code'
  2592.     assume cs:_text
  2593.  
  2594.     public    _absdr        ; absolute disk read
  2595. _absdr    proc    near
  2596.     push    si
  2597.     push    di
  2598.     push    bp
  2599.     mov    bp,sp
  2600.  
  2601.     mov    al,[bp+8]    ; drive number
  2602.     mov    cx,[bp+10]    ; number of sectors to read
  2603.     mov    dx,[bp+12]    ; beginning sector number
  2604.     mov    bx,[bp+14]    ; buffer address
  2605.     int    25h        ; absolute disk read
  2606. ; int 25h supposed to destroy all registers except segment registers
  2607.     jc    error        ; if error, leave error no in AX
  2608.     mov    ax,0        ; clear error number if no error
  2609. error:
  2610.     inc    sp        ; pop the flags
  2611.     inc    sp        ; pop the flags
  2612.     pop    bp
  2613.     pop    di
  2614.     pop    si
  2615.     ret
  2616. _absdr    endp
  2617.  
  2618.     public    _absdw        ; absolute disk write
  2619. _absdw    proc    near
  2620.     push    si
  2621.     push    di
  2622.     push    bp
  2623.     mov    bp,sp
  2624.  
  2625.     mov    al,[bp+8]    ; drive number
  2626.     mov    cx,[bp+10]    ; number of sectors to read
  2627.     mov    dx,[bp+12]    ; beginning sector number
  2628.     mov    bx,[bp+14]    ; buffer address
  2629.     int    26h        ; absolute disk read
  2630. ; int 26h supposed to destroy all registers except segment registers
  2631.     jc    error1        ; if error, leave error no in AX
  2632.     mov    ax,0        ; clear error number if no error
  2633. error1:
  2634.     inc    sp        ; pop the flags
  2635.     inc    sp        ; pop the flags
  2636.     pop    bp
  2637.     pop    di
  2638.     pop    si
  2639.     ret
  2640. _absdw    endp
  2641.  
  2642. _text    ends
  2643.     end
  2644.  8-Sep-86 16:33:37-PDT,1389;000000000000
  2645. Return-Path: <pwu@unix.macc.wisc.edu>
  2646. Received: FROM UNIX.MACC.WISC.EDU BY B.ISI.EDU WITH TCP ; 8 Sep 86 16:33:05 PDT
  2647. Received: by unix.macc.wisc.edu;
  2648.           id AA04941; 4.12/5; Mon, 8 Sep 86 17:31:04 cdt
  2649. Date: Mon, 8 Sep 86 17:31:04 cdt
  2650. From: Peter Wu <pwu@unix.macc.wisc.edu>
  2651. Message-Id: <8609082231.AA04941@unix.macc.wisc.edu>
  2652. To: info-ibmpc-request@mosis
  2653. Subject: error.c
  2654.  
  2655. /* handle internal errors */
  2656.  
  2657.  
  2658. /* global variable */
  2659. int brk_st;  /* orginal break status */
  2660.  
  2661. error(where,panic)
  2662. char *where;  /* call from what procedure */
  2663. int panic;  /* should I tell the user to panic? 0=no 1=yes */
  2664. {
  2665.   putn("MV stopped due to internal error in '", where, "'\n\015", 0);
  2666.   if (panic) {    /* this should never happen */
  2667.     putn("Because of this error, your disk directory might be in an\n\015",
  2668.      "inconsistent state. Please run chkdsk on your disk to see\n\015",
  2669.      "if this is so. Sorry.\n\015", 0);
  2670.   } else {  /* not to panic */
  2671.     putn("Do not worry, your disk is not screwed up, only that the\n\015",
  2672.      "operation you requested was not completed.\n\015", 0);
  2673.   }
  2674.   putn("Please report this error to the author of this program. You can\n\015",
  2675.        "obtain his email address by typing 'mv' with no parameters.\n\015", 0);
  2676.  
  2677.   /* now restore break status to whatever the user set it to */
  2678.   bset(brk_st);
  2679.   exit(2);  /* indicate internal error */
  2680. }
  2681.